1#define _POSIX_C_SOURCE 200809
2#define _XOPEN_SOURCE 700
3#include <omp.h>
4#include <limits.h>
5#include <stdlib.h>
6#include <stdbool.h>
7#include <dlfcn.h>
8#include <string.h>
9#include <errno.h>
10#include <sys/wait.h>
11#include <sys/stat.h>
12#include <sys/types.h>
13#include <unistd.h>
14#include <spawn.h>
15#include <time.h>
16#include <stdio.h>
17#include "effects.h"
18#include "log.h"
19
20// glib might or might not have already defined MIN,
21// depending on whether we have pixbuf or not...
22#ifndef MIN
23#define MIN(a, b) ((a) < (b) ? (a) : (b))
24#endif
25
26extern char **environ;
27
28static int screen_size_to_pix(struct swaylock_effect_screen_pos size, int screensize, int scale) {
29 if (size.is_percent) {
30 return (size.pos / 100.0) * screensize;
31 } else if (size.pos > 0) {
32 return size.pos * scale;
33 } else {
34 return size.pos;
35 }
36}
37
38static int screen_pos_to_pix(struct swaylock_effect_screen_pos pos, int screensize, int scale) {
39 int actual;
40 if (pos.is_percent) {
41 actual = (pos.pos / 100.0) * screensize;
42 } else {
43 actual = pos.pos * scale;
44 }
45
46 if (actual < 0) {
47 actual = screensize + actual;
48 }
49
50 return actual;
51}
52
53static const char *effect_name(struct swaylock_effect *effect) {
54 switch (effect->tag) {
55 case EFFECT_BLUR: return "blur";
56 case EFFECT_PIXELATE: return "pixelate";
57 case EFFECT_SCALE: return "scale";
58 case EFFECT_GREYSCALE: return "greyscale";
59 case EFFECT_VIGNETTE: return "vignette";
60 case EFFECT_COMPOSE: return "compose";
61 case EFFECT_CUSTOM: return effect->e.custom;
62 }
63
64 abort();
65}
66
67static void screen_pos_pair_to_pix(
68 struct swaylock_effect_screen_pos posx,
69 struct swaylock_effect_screen_pos posy,
70 int objwidth, int objheight,
71 int screenwidth, int screenheight, int scale, int gravity,
72 int *outx, int *outy) {
73 int x = screen_pos_to_pix(posx, screenwidth, scale);
74 int y = screen_pos_to_pix(posy, screenheight, scale);
75
76 // Adjust X
77 switch (gravity) {
78 case EFFECT_COMPOSE_GRAV_CENTER:
79 case EFFECT_COMPOSE_GRAV_N:
80 case EFFECT_COMPOSE_GRAV_S:
81 x -= objwidth / 2;
82 break;
83 case EFFECT_COMPOSE_GRAV_NW:
84 case EFFECT_COMPOSE_GRAV_SW:
85 case EFFECT_COMPOSE_GRAV_W:
86 break;
87 case EFFECT_COMPOSE_GRAV_NE:
88 case EFFECT_COMPOSE_GRAV_SE:
89 case EFFECT_COMPOSE_GRAV_E:
90 x -= objwidth;
91 break;
92 }
93
94 // Adjust Y
95 switch (gravity) {
96 case EFFECT_COMPOSE_GRAV_CENTER:
97 case EFFECT_COMPOSE_GRAV_W:
98 case EFFECT_COMPOSE_GRAV_E:
99 y -= objheight / 2;
100 break;
101 case EFFECT_COMPOSE_GRAV_NW:
102 case EFFECT_COMPOSE_GRAV_NE:
103 case EFFECT_COMPOSE_GRAV_N:
104 break;
105 case EFFECT_COMPOSE_GRAV_SW:
106 case EFFECT_COMPOSE_GRAV_SE:
107 case EFFECT_COMPOSE_GRAV_S:
108 y -= objheight;
109 break;
110 }
111
112 *outx = x;
113 *outy = y;
114}
115
116static uint32_t blend_pixels(float alpha, uint32_t srcpix, uint32_t destpix) {
117 uint8_t srcr = (srcpix & 0x00ff0000) >> 16;
118 uint8_t destr = (destpix & 0x00ff0000) >> 16;
119 uint8_t srcg = (srcpix & 0x0000ff00) >> 8;
120 uint8_t destg = (destpix & 0x0000ff00) >> 8;
121 uint8_t srcb = (srcpix & 0x000000ff) >> 0;
122 uint8_t destb = (destpix & 0x000000ff) >> 0;
123
124 return (uint32_t)0 |
125 (uint32_t)255 << 24 |
126 (uint32_t)(srcr + destr * (1 - alpha)) << 16 |
127 (uint32_t)(srcg + destg * (1 - alpha)) << 8 |
128 (uint32_t)(srcb + destb * (1 - alpha)) << 0;
129}
130
131static void blur_h(uint32_t *dest, uint32_t *src, int width, int height,
132 int radius) {
133 const int minradius = radius < width ? radius : width;
134
135#pragma omp parallel for
136 for (int y = 0; y < height; ++y) {
137 uint32_t *srow = src + y * width;
138 uint32_t *drow = dest + y * width;
139
140 // 'range' is float, because floating point division is usually faster
141 // than integer division.
142 int r_acc = 0;
143 int g_acc = 0;
144 int b_acc = 0;
145 float range = minradius;
146
147 // Accumulate the range (0..radius)
148 for (int x = 0; x < minradius; ++x) {
149 r_acc += (srow[x] & 0xff0000) >> 16;
150 g_acc += (srow[x] & 0x00ff00) >> 8;
151 b_acc += (srow[x] & 0x0000ff);
152 }
153
154 // Deal with the main body
155 for (int x = 0; x < width; ++x) {
156 if (x >= minradius) {
157 r_acc -= (srow[x - radius] & 0xff0000) >> 16;
158 g_acc -= (srow[x - radius] & 0x00ff00) >> 8;
159 b_acc -= (srow[x - radius] & 0x0000ff);
160 range -= 1;
161 }
162
163 if (x < width - minradius) {
164 r_acc += (srow[x + radius] & 0xff0000) >> 16;
165 g_acc += (srow[x + radius] & 0x00ff00) >> 8;
166 b_acc += (srow[x + radius] & 0x0000ff);
167 range += 1;
168 }
169
170 drow[x] = 0 |
171 (int)(r_acc / range) << 16 |
172 (int)(g_acc / range) << 8 |
173 (int)(b_acc / range);
174 }
175 }
176}
177
178static void blur_v(uint32_t *dest, uint32_t *src, int width, int height,
179 int radius) {
180 const int minradius = radius < height ? radius : height;
181
182#pragma omp parallel for
183 for (int x = 0; x < width; ++x) {
184 uint32_t *scol = src + x;
185 uint32_t *dcol = dest + x;
186
187 // 'range' is float, because floating point division is usually faster
188 // than integer division.
189 int r_acc = 0;
190 int g_acc = 0;
191 int b_acc = 0;
192 float range = minradius;
193
194 // Accumulate the range (0..radius)
195 for (int y = 0; y < minradius; ++y) {
196 r_acc += (scol[y * width] & 0xff0000) >> 16;
197 g_acc += (scol[y * width] & 0x00ff00) >> 8;
198 b_acc += (scol[y * width] & 0x0000ff);
199 }
200
201 // Deal with the main body
202 for (int y = 0; y < height; ++y) {
203 if (y >= minradius) {
204 r_acc -= (scol[(y - radius) * width] & 0xff0000) >> 16;
205 g_acc -= (scol[(y - radius) * width] & 0x00ff00) >> 8;
206 b_acc -= (scol[(y - radius) * width] & 0x0000ff);
207 range -= 1;
208 }
209
210 if (y < height - minradius) {
211 r_acc += (scol[(y + radius) * width] & 0xff0000) >> 16;
212 g_acc += (scol[(y + radius) * width] & 0x00ff00) >> 8;
213 b_acc += (scol[(y + radius) * width] & 0x0000ff);
214 range += 1;
215 }
216
217 dcol[y * width] = 0 |
218 (int)(r_acc / range) << 16 |
219 (int)(g_acc / range) << 8 |
220 (int)(b_acc / range);
221 }
222 }
223}
224
225static void blur_once(uint32_t *dest, uint32_t *src, uint32_t *scratch,
226 int width, int height, int radius) {
227 blur_h(scratch, src, width, height, radius);
228 blur_v(dest, scratch, width, height, radius);
229}
230
231// This effect_blur function, and the associated blur_* functions,
232// are my own adaptations of code in yvbbrjdr's i3lock-fancy-rapid:
233// https://github.com/yvbbrjdr/i3lock-fancy-rapid
234static void effect_blur(uint32_t *dest, uint32_t *src, int width, int height, int scale,
235 int radius, int times) {
236 uint32_t *origdest = dest;
237
238 uint32_t *scratch = malloc(width * height * sizeof(*scratch));
239 blur_once(dest, src, scratch, width, height, radius * scale);
240 for (int i = 0; i < times - 1; ++i) {
241 uint32_t *tmp = src;
242 src = dest;
243 dest = tmp;
244 blur_once(dest, src, scratch, width, height, radius * scale);
245 }
246 free(scratch);
247
248 // We're flipping between using dest and src;
249 // if the last buffer we used was src, copy that over to dest.
250 if (dest != origdest)
251 memcpy(origdest, dest, width * height * sizeof(*dest));
252}
253
254static void effect_pixelate(uint32_t *data, int width, int height, int scale, int factor) {
255 factor *= scale;
256#pragma omp parallel for
257 for (int y = 0; y < height / factor + 1; ++y) {
258 for (int x = 0; x < width / factor + 1; ++x) {
259 int total_r = 0, total_g = 0, total_b = 0;
260
261 int xstart = x * factor;
262 int ystart = y * factor;
263 int xlim = MIN(xstart + factor, width);
264 int ylim = MIN(ystart + factor, height);
265
266 // Average
267 for (int ry = ystart; ry < ylim; ++ry) {
268 for (int rx = xstart; rx < xlim; ++rx) {
269 int index = ry * width + rx;
270 total_r += (data[index] & 0xff0000) >> 16;
271 total_g += (data[index] & 0x00ff00) >> 8;
272 total_b += (data[index] & 0x0000ff);
273 }
274 }
275
276 int r = total_r / (factor * factor);
277 int g = total_g / (factor * factor);
278 int b = total_b / (factor * factor);
279
280 // Fill pixels
281 for (int ry = ystart; ry < ylim; ++ry) {
282 for (int rx = xstart; rx < xlim; ++rx) {
283 int index = ry * width + rx;
284 data[index] = r << 16 | g << 8 | b;
285 }
286 }
287 }
288 }
289}
290
291static void effect_scale(uint32_t *dest, uint32_t *src, int swidth, int sheight,
292 double scale) {
293 int dwidth = swidth * scale;
294 int dheight = sheight * scale;
295 double fact = 1.0 / scale;
296
297#pragma omp parallel for
298 for (int dy = 0; dy < dheight; ++dy) {
299 int sy = dy * fact;
300 if (sy >= sheight) continue;
301 for (int dx = 0; dx < dwidth; ++dx) {
302 int sx = dx * fact;
303 if (sx >= swidth) continue;
304 dest[dy * dwidth + dx] = src[sy * swidth + sx];
305 }
306 }
307}
308
309static void effect_greyscale(uint32_t *data, int width, int height) {
310#pragma omp parallel for
311 for (int y = 0; y < height; ++y) {
312 for (int x = 0; x < width; ++x) {
313 int index = y * width + x;
314 int r = (data[index] & 0xff0000) >> 16;
315 int g = (data[index] & 0x00ff00) >> 8;
316 int b = (data[index] & 0x0000ff);
317 int luma = 0.2989 * r + 0.5870 * g + 0.1140 * b;
318 if (luma < 0) luma = 0;
319 if (luma > 255) luma = 255;
320 luma &= 0xFF;
321 data[index] = luma << 16 | luma << 8 | luma;
322 }
323 }
324}
325
326static void effect_vignette(uint32_t *data, int width, int height,
327 double base, double factor) {
328 base = fmin(1, fmax(0, base));
329 factor = fmin(1 - base, fmax(0, factor));
330#pragma omp parallel for
331 for (int y = 0; y < height; ++y) {
332 for (int x = 0; x < width; ++x) {
333
334 double xf = (x * 1.0) / width;
335 double yf = (y * 1.0) / height;
336 double vignette_factor = base + factor
337 * 16 * xf * yf * (1.0 - xf) * (1.0 - yf);
338
339 int index = y * width + x;
340 int r = (data[index] & 0xff0000) >> 16;
341 int g = (data[index] & 0x00ff00) >> 8;
342 int b = (data[index] & 0x0000ff);
343
344 r = (int)(r * vignette_factor) & 0xFF;
345 g = (int)(g * vignette_factor) & 0xFF;
346 b = (int)(b * vignette_factor) & 0xFF;
347
348 data[index] = r << 16 | g << 8 | b;
349 }
350 }
351}
352
353static void effect_compose(uint32_t *data, int width, int height, int scale,
354 struct swaylock_effect_screen_pos posx,
355 struct swaylock_effect_screen_pos posy,
356 struct swaylock_effect_screen_pos posw,
357 struct swaylock_effect_screen_pos posh,
358 int gravity, char *imgpath) {
359#if !HAVE_GDK_PIXBUF
360 (void)&blend_pixels;
361 (void)&screen_size_to_pix;
362 (void)&screen_pos_pair_to_pix;
363 swaylock_log(LOG_ERROR, "Compose effect: Compiled without gdk_pixbuf support.\n");
364 return;
365#else
366 int imgw = screen_size_to_pix(posw, width, scale);
367 int imgh = screen_size_to_pix(posh, height, scale);
368 bool preserve_aspect = imgw < 0 || imgh < 0;
369
370 GError *err = NULL;
371 GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file_at_scale(
372 imgpath, imgw, imgh, preserve_aspect, &err);
373 if (!pixbuf) {
374 swaylock_log(LOG_ERROR, "Compose effect: Failed to load image file '%s' (%s).",
375 imgpath, err->message);
376 g_error_free(err);
377 return;
378 }
379
380 cairo_surface_t *image = gdk_cairo_image_surface_create_from_pixbuf(pixbuf);
381 g_object_unref(pixbuf);
382
383 int bufw = cairo_image_surface_get_width(image);
384 int bufh = cairo_image_surface_get_height(image);
385 uint32_t *bufdata = (uint32_t *)cairo_image_surface_get_data(image);
386 int bufstride = cairo_image_surface_get_stride(image) / 4;
387 bool bufalpha = cairo_image_surface_get_format(image) == CAIRO_FORMAT_ARGB32;
388
389 int imgx, imgy;
390 screen_pos_pair_to_pix(
391 posx, posy, bufw, bufh,
392 width, height, scale, gravity,
393 &imgx, &imgy);
394
395#pragma omp parallel for
396 for (int offy = 0; offy < bufh; ++offy) {
397 if (offy + imgy < 0 || offy + imgy > height)
398 continue;
399
400 for (int offx = 0; offx < bufw; ++offx) {
401 if (offx + imgx < 0 || offx + imgx > width)
402 continue;
403
404 size_t idx = (size_t)(offy + imgy) * width + (offx + imgx);
405 size_t bufidx = (size_t)offy * bufstride + (offx);
406
407 if (!bufalpha) {
408 data[idx] = bufdata[bufidx];
409 } else {
410 uint8_t alpha = (bufdata[bufidx] & 0xff000000) >> 24;
411 if (alpha == 255) {
412 data[idx] = bufdata[bufidx];
413 } else if (alpha != 0) {
414 data[idx] = blend_pixels(alpha / 255.0, bufdata[bufidx], data[idx]);
415 }
416 }
417 }
418 }
419
420 cairo_surface_destroy(image);
421#endif
422}
423
424static void effect_custom_run(uint32_t *data, int width, int height, int scale,
425 char *path) {
426 void *dl = dlopen(path, RTLD_LAZY);
427 if (dl == NULL) {
428 swaylock_log(LOG_ERROR, "Custom effect: %s", dlerror());
429 return;
430 }
431
432 void (*effect_func)(uint32_t *data, int width, int height, int scale) =
433 dlsym(dl, "swaylock_effect");
434 if (effect_func != NULL) {
435 effect_func(data, width, height, scale);
436 dlclose(dl);
437 return;
438 }
439
440 uint32_t (*pixel_func)(uint32_t pix, int x, int y, int width, int height) =
441 dlsym(dl, "swaylock_pixel");
442 if (pixel_func != NULL) {
443#pragma omp parallel for
444 for (int y = 0; y < height; ++y) {
445 for (int x = 0; x < width; ++x) {
446 data[y * width + x] =
447 pixel_func(data[y * width + x], x, y, width, height);
448 }
449 }
450
451 dlclose(dl);
452 return;
453 }
454
455 (void)dlsym(dl, "swaylock_effect"); // Change the result of dlerror()
456 swaylock_log(LOG_ERROR, "Custom effect: %s", dlerror());
457}
458
459static bool file_is_outdated(const char *input, const char *output) {
460 struct stat instat, outstat;
461 if (stat(input, &instat) < 0) {
462 return true;
463 }
464
465 if (stat(output, &outstat) < 0) {
466 return true;
467 }
468
469 if (instat.st_mtim.tv_sec > outstat.st_mtim.tv_sec) {
470 return true;
471 }
472
473 if (
474 instat.st_mtim.tv_sec == outstat.st_mtim.tv_sec &&
475 instat.st_mtim.tv_nsec >= outstat.st_mtim.tv_nsec) {
476 return true;
477 }
478
479 return false;
480}
481
482static char *effect_custom_compile(const char *path) {
483 static char *cachepath = NULL;
484 static size_t cachelen;
485 if (!cachepath) {
486 char *xdgdir = getenv("XDG_DATA_HOME");
487 if (xdgdir) {
488 cachepath = malloc(strlen(xdgdir) + strlen("/swaylock") + 1);
489 cachelen = sprintf(cachepath, "%s/swaylock", xdgdir);
490 } else {
491 char *homedir = getenv("HOME");
492 if (homedir == NULL) {
493 swaylock_log(LOG_ERROR,
494 "Can't compile custom effect; neither $HOME nor $XDG_CONFIG_HOME "
495 "is defined.");
496 return NULL;
497 }
498
499 cachepath = malloc(strlen(homedir) + strlen("/.cache/swaylock") + 1);
500 cachelen = sprintf(cachepath, "%s/.cache/swaylock", homedir);
501 }
502
503 if (mkdir(cachepath, 0777) < 0 && errno != EEXIST) {
504 swaylock_log(LOG_ERROR,
505 "Can't compile custom effect; mkdir %s failed: %s\n",
506 cachepath, strerror(errno));
507 free(cachepath);
508 cachepath = NULL;
509 return NULL;
510 }
511 }
512
513 // Find the true, absolute path of the input file
514 char *abspath = realpath(path, NULL);
515 size_t abspathlen = strlen(abspath);
516
517 char *outpath = malloc(cachelen + 1 + abspathlen + 3 + 1);
518 size_t outlen = sprintf(outpath, "%s/%s.so", cachepath, abspath);
519
520 // Sanitize
521 for (char *ch = outpath + cachelen + 1; ch < outpath + cachelen + 1 + abspathlen; ++ch) {
522 if (!(
523 (*ch >= 'a' && *ch <= 'z') ||
524 (*ch >= 'A' && *ch <= 'Z') ||
525 (*ch >= '0' && *ch <= '9') ||
526 (*ch == '.'))) {
527 *ch = '_';
528 }
529 }
530
531 if (!file_is_outdated(path, outpath)) {
532 free(abspath);
533 return outpath;
534 }
535
536 static const char *fmt = "cc -shared -g -O2 -march=native -fopenmp -o '%s' '%s' -lm";
537 char *cmd = malloc(strlen(fmt) + outlen - 2 + abspathlen - 2 + 1);
538 sprintf(cmd, fmt, outpath, abspath);
539 free(abspath);
540 fprintf(stderr, "Compiling custom effect: %s\n", cmd);
541
542 // Finally, compile.
543 int ret = system(cmd);
544 free(cmd);
545 if (ret != 0) {
546 if (ret == -1) {
547 swaylock_log(LOG_ERROR, "Custom effect: system(): %s", strerror(errno));
548 free(outpath);
549 return NULL;
550 } else {
551 swaylock_log(LOG_ERROR, "Custom effect compilation failed\n");
552 free(outpath);
553 return NULL;
554 }
555 }
556
557 return outpath;
558}
559
560static void effect_custom(uint32_t *data, int width, int height, int scale,
561 char *path) {
562 size_t pathlen = strlen(path);
563 if (pathlen > 3 && strcmp(path + pathlen - 3, ".so") == 0) {
564 effect_custom_run(data, width, height, scale, path);
565 } else if (pathlen > 2 && strcmp(path + pathlen - 2, ".c") == 0) {
566 char *compiled = effect_custom_compile(path);
567 if (compiled != NULL) {
568 effect_custom_run(data, width, height, scale, compiled);
569 free(compiled);
570 }
571 } else {
572 swaylock_log(
573 LOG_ERROR, "%s: Unknown file type for custom effect (expected .c or .so)",
574 path);
575 }
576}
577
578static cairo_surface_t *run_effect(cairo_surface_t *surface, int scale,
579 struct swaylock_effect *effect) {
580 switch (effect->tag) {
581 case EFFECT_BLUR: {
582 cairo_surface_t *surf = cairo_image_surface_create(
583 CAIRO_FORMAT_RGB24,
584 cairo_image_surface_get_width(surface),
585 cairo_image_surface_get_height(surface));
586
587 if (cairo_surface_status(surf) != CAIRO_STATUS_SUCCESS) {
588 swaylock_log(LOG_ERROR, "Failed to create surface for blur effect");
589 cairo_surface_destroy(surf);
590 break;
591 }
592
593 effect_blur(
594 (uint32_t *)cairo_image_surface_get_data(surf),
595 (uint32_t *)cairo_image_surface_get_data(surface),
596 cairo_image_surface_get_width(surface),
597 cairo_image_surface_get_height(surface),
598 scale,
599 effect->e.blur.radius, effect->e.blur.times);
600 cairo_surface_flush(surf);
601 cairo_surface_destroy(surface);
602 surface = surf;
603 break;
604 }
605
606 case EFFECT_PIXELATE: {
607 effect_pixelate(
608 (uint32_t *)cairo_image_surface_get_data(surface),
609 cairo_image_surface_get_width(surface),
610 cairo_image_surface_get_height(surface),
611 scale,
612 effect->e.pixelate.factor);
613 cairo_surface_flush(surface);
614 break;
615 }
616
617 case EFFECT_SCALE: {
618 cairo_surface_t *surf = cairo_image_surface_create(
619 CAIRO_FORMAT_RGB24,
620 cairo_image_surface_get_width(surface) * effect->e.scale,
621 cairo_image_surface_get_height(surface) * effect->e.scale);
622
623 if (cairo_surface_status(surf) != CAIRO_STATUS_SUCCESS) {
624 swaylock_log(LOG_ERROR, "Failed to create surface for scale effect");
625 cairo_surface_destroy(surf);
626 break;
627 }
628
629 effect_scale(
630 (uint32_t *)cairo_image_surface_get_data(surf),
631 (uint32_t *)cairo_image_surface_get_data(surface),
632 cairo_image_surface_get_width(surface),
633 cairo_image_surface_get_height(surface),
634 effect->e.scale);
635 cairo_surface_flush(surf);
636 cairo_surface_destroy(surface);
637 surface = surf;
638 break;
639 }
640
641 case EFFECT_GREYSCALE: {
642 effect_greyscale(
643 (uint32_t *)cairo_image_surface_get_data(surface),
644 cairo_image_surface_get_width(surface),
645 cairo_image_surface_get_height(surface));
646 cairo_surface_flush(surface);
647 break;
648 }
649
650 case EFFECT_VIGNETTE: {
651 effect_vignette(
652 (uint32_t *)cairo_image_surface_get_data(surface),
653 cairo_image_surface_get_width(surface),
654 cairo_image_surface_get_height(surface),
655 effect->e.vignette.base,
656 effect->e.vignette.factor);
657 cairo_surface_flush(surface);
658 break;
659 }
660
661 case EFFECT_COMPOSE: {
662 effect_compose(
663 (uint32_t *)cairo_image_surface_get_data(surface),
664 cairo_image_surface_get_width(surface),
665 cairo_image_surface_get_height(surface),
666 scale,
667 effect->e.compose.x, effect->e.compose.y,
668 effect->e.compose.w, effect->e.compose.h,
669 effect->e.compose.gravity, effect->e.compose.imgpath);
670 cairo_surface_flush(surface);
671 break;
672 }
673
674 case EFFECT_CUSTOM: {
675 effect_custom(
676 (uint32_t *)cairo_image_surface_get_data(surface),
677 cairo_image_surface_get_width(surface),
678 cairo_image_surface_get_height(surface),
679 scale,
680 effect->e.custom);
681 cairo_surface_flush(surface);
682 break;
683 } }
684
685 return surface;
686}
687
688static cairo_surface_t *ensure_format(cairo_surface_t *surface) {
689 if (cairo_image_surface_get_format(surface) == CAIRO_FORMAT_RGB24) {
690 return surface;
691 }
692
693 swaylock_log(LOG_DEBUG, "Have to convert surface to CAIRO_FORMAT_RGB24 from %i.",
694 (int)cairo_image_surface_get_format(surface));
695
696 cairo_surface_t *surf = cairo_image_surface_create(
697 CAIRO_FORMAT_RGB24,
698 cairo_image_surface_get_width(surface),
699 cairo_image_surface_get_height(surface));
700 if (cairo_surface_status(surf) != CAIRO_STATUS_SUCCESS) {
701 swaylock_log(LOG_ERROR, "Failed to create surface for scale effect");
702 cairo_surface_destroy(surf);
703 return NULL;
704 }
705
706 memcpy(
707 cairo_image_surface_get_data(surf),
708 cairo_image_surface_get_data(surface),
709 cairo_image_surface_get_stride(surface) * cairo_image_surface_get_height(surface));
710 cairo_surface_destroy(surface);
711 return surf;
712}
713
714cairo_surface_t *swaylock_effects_run(cairo_surface_t *surface, int scale,
715 struct swaylock_effect *effects, int count) {
716 surface = ensure_format(surface);
717 if (surface == NULL) return NULL;
718
719 for (int i = 0; i < count; ++i) {
720 struct swaylock_effect *effect = &effects[i];
721 surface = run_effect(surface, scale, effect);
722 }
723
724 return surface;
725}
726
727#define TIME_MSEC(tv) ((tv).tv_sec * 1000.0 + (tv).tv_nsec / 1000000.0)
728#define TIME_DELTA(first, last) (TIME_MSEC(last) - TIME_MSEC(first))
729
730cairo_surface_t *swaylock_effects_run_timed(cairo_surface_t *surface, int scale,
731 struct swaylock_effect *effects, int count) {
732 struct timespec start_tv;
733 clock_gettime(CLOCK_MONOTONIC, &start_tv);
734
735 surface = ensure_format(surface);
736 if (surface == NULL) return NULL;
737
738 fprintf(stderr, "Running %i effects:\n", count);
739 for (int i = 0; i < count; ++i) {
740 struct timespec effect_start_tv;
741 clock_gettime(CLOCK_MONOTONIC, &effect_start_tv);
742
743 struct swaylock_effect *effect = &effects[i];
744 surface = run_effect(surface, scale, effect);
745
746 struct timespec effect_end_tv;
747 clock_gettime(CLOCK_MONOTONIC, &effect_end_tv);
748 fprintf(stderr, " %s: %fms\n", effect_name(effect),
749 TIME_DELTA(effect_start_tv, effect_end_tv));
750 }
751
752 struct timespec end_tv;
753 clock_gettime(CLOCK_MONOTONIC, &end_tv);
754 fprintf(stderr, "Effects took %fms.\n", TIME_DELTA(start_tv, end_tv));
755
756 return surface;
757}