├── .build.yml ├── .gitignore ├── LICENSE ├── README.md ├── background-image.c ├── cairo.c ├── comm.c ├── completions ├── bash │ └── swaylock ├── fish │ └── swaylock.fish └── zsh │ └── _swaylock ├── effects.c ├── fade.c ├── include ├── background-image.h ├── cairo.h ├── comm.h ├── effects.h ├── fade.h ├── log.h ├── loop.h ├── meson.build ├── pool-buffer.h ├── seat.h ├── swaylock.h └── unicode.h ├── log.c ├── loop.c ├── main.c ├── meson.build ├── meson_options.txt ├── pam.c ├── pam └── swaylock ├── password.c ├── pool-buffer.c ├── render.c ├── screenshot.png ├── seat.c ├── shadow.c ├── swaylock.1.scd ├── unicode.c ├── wlr-input-inhibitor-unstable-v1.xml ├── wlr-layer-shell-unstable-v1.xml └── wlr-screencopy-unstable-v1.xml /.build.yml: -------------------------------------------------------------------------------- 1 | image: alpine/edge 2 | packages: 3 | - meson 4 | - cairo-dev 5 | - wayland-dev 6 | - wayland-protocols 7 | - libxkbcommon-dev 8 | - gdk-pixbuf-dev 9 | - linux-pam-dev 10 | - scdoc 11 | sources: 12 | - https://github.com/swaywm/swaylock 13 | tasks: 14 | - setup: | 15 | cd swaylock 16 | meson build 17 | - build: | 18 | cd swaylock 19 | ninja -C build 20 | - build-no-pam: | 21 | cd swaylock 22 | meson configure build -Dpam=disabled 23 | ninja -C build 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016-2019 Drew DeVault 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to do 8 | so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # swaylock-effects 2 | 3 | Swaylock-effects is a fork of [swaylock](https://github.com/swaywm/swaylock) 4 | which adds built-in screenshots and image manipulation effects like blurring. 5 | It's inspired by [i3lock-color](https://github.com/PandorasFox/i3lock-color), 6 | although the feature sets aren't perfectly overlapping. 7 | 8 | ![Screenshot](https://raw.githubusercontent.com/mortie/swaylock-effects/master/screenshot.png) 9 | 10 | ## Example Command 11 | 12 | swaylock \ 13 | --screenshots \ 14 | --clock \ 15 | --indicator \ 16 | --indicator-radius 100 \ 17 | --indicator-thickness 7 \ 18 | --effect-blur 7x5 \ 19 | --effect-vignette 0.5:0.5 \ 20 | --ring-color bb00cc \ 21 | --key-hl-color 880033 \ 22 | --line-color 00000000 \ 23 | --inside-color 00000088 \ 24 | --separator-color 00000000 \ 25 | --grace 2 \ 26 | --fade-in 0.2 27 | 28 | ## New Features 29 | 30 | The main new features compared to upstream swaylock are: 31 | 32 | * `--screenshots` to use screenshots instead of an image on disk or a color 33 | * `--clock` to show date/time in the indicator 34 | * Use `--indicator` to make the indicator always active 35 | * Use `--timestr` and `--datestr` to set the date/time formats 36 | (using strftime-style formatting) 37 | * `--submit-on-touch` to use your touchscreen to submit a password. 38 | If you can unlock your device with anything else than your password, 39 | this might come helpful to trigger PAM's authentication process. 40 | * `--grace ` to set a password grace period, so that the password 41 | isn't required to unlock until some number of seconds have passed. 42 | * Used together with `--indicator`, the indicator is always shown, 43 | even in the grace period. 44 | * Used together with `--indicator-idle-visible`, the indicator is only 45 | visible after the grace period. 46 | * By default, a key press, a mouse event or a touch event will unlock 47 | during the grace period. Use `--grace-no-mouse` to not unlock as a response 48 | to a mouse event, and `--grace-no-touch` to not unlock as a response to 49 | a touch event. 50 | * `--fade-in ` to make the lock screen fade in. 51 | * Various effects which can be applied to the background image 52 | * `--effect-blur x`: Blur the image (thanks to yvbbrjdr's 53 | fast box blur algorithm in 54 | [i3lock-fancy-rapid](https://github.com/yvbbrjdr/i3lock-fancy-rapid)) 55 | * `--effect-pixelate `: Pixelate the image. 56 | * `--effect-scale `: Scale the image by a factor. This can be used 57 | to make other effects faster if you don't need the full resolution. 58 | * `--effect-greyscale`: Make the image greyscale. 59 | * `--effect-vignette :`: Apply a vignette effect (range is 0-1). 60 | * `--effect-compose ;;;`: Overlay another image. 61 | * `--effect-custom `: Load a custom effect from a C file or shared object. 62 | 63 | New feature ideas are welcome as issues (though I may never get around to 64 | implement them), new feature implementations are welcome as pull requests :) 65 | 66 | ## Installation 67 | 68 | ### From Packages 69 | 70 | * Alpine Linux: [swaylock-effects](https://pkgs.alpinelinux.org/packages?name=swaylock-effects) 71 | * Arch Linux (AUR): [swaylock-effects-git](https://aur.archlinux.org/packages/swaylock-effects-git/) 72 | * Fedora (Copr): [swaylock-effects](https://copr.fedorainfracloud.org/coprs/eddsalkield/swaylock-effects/) 73 | (originally by Edd Salkield, now looking for a new maintainer) 74 | * FreeBSD: [swaylock-effects](https://www.freshports.org/x11/swaylock-effects/) 75 | * Gentoo (GURU overlay): [swaylock-effects](https://gpo.zugaina.org/Overlays/guru/gui-apps/swaylock-effects) 76 | * T2 SDE: [swaylock-effects](https://t2sde.org/packages/swaylock-effects) 77 | 78 | ### Compiling from Source 79 | 80 | Install dependencies: 81 | 82 | * meson \* 83 | * wayland 84 | * wayland-protocols \* 85 | * libxkbcommon 86 | * cairo 87 | * gdk-pixbuf2 \*\* 88 | * pam (optional) 89 | * [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (optional: man pages) \* 90 | * git \* 91 | * openmp (if using a compiler other than GCC) 92 | 93 | _\*Compile-time dep_ 94 | 95 | _\*\*Optional: required for background images other than PNG_ 96 | 97 | Run these commands: 98 | 99 | meson build 100 | ninja -C build 101 | sudo ninja -C build install 102 | 103 | On systems without PAM, you need to suid the swaylock binary: 104 | 105 | sudo chmod a+s /usr/local/bin/swaylock 106 | 107 | Swaylock will drop root permissions shortly after startup. 108 | 109 | ## Effects 110 | 111 | ### Blur 112 | 113 | `--effect-blur x`: Blur the image. 114 | 115 | `` is a number specifying how big 116 | the blur is, `` is a number which specifies essentially how high quality the blur is 117 | (i.e how closely the effect will resemble a true gaussian blur). 118 | 119 | ### Pixelate 120 | 121 | `--effect-pixelate `: Pixelate the image. 122 | 123 | `` is the amount of pixelation; a value of 10 will make each 10x10 square of pixels 124 | the same color. 125 | 126 | ### Scale 127 | 128 | `--effect-scale `: Scale the image by a factor. 129 | 130 | This effect scales the internal buffer. This has a few uses: 131 | 132 | * Use `--effect-scale` in combination with `--scaling` to create a zoom effect: 133 | `--effect-scale 1.1 --scaling center` 134 | * Speed up other effects by making the resolution smaller: with 135 | `--effect-scale 0.5 --effect-blur 7x5 --effect-scale 2`, swaylock-effect needs to blur 136 | only 1/4 as many pixels. 137 | 138 | ### Greyscale 139 | 140 | `--effect-greyscale`: Make the displayed image greyscale. 141 | 142 | ### Vignette 143 | 144 | `--effect-vignette :`: Apply a vignette effect. 145 | Base and factor should be between 0 and 1. 146 | 147 | ### Compose 148 | 149 | `--effect-compose ";;;"`: Overlay another image to your lock screen. 150 | 151 | * ``: Optional. The position on the screen to put the image, as `,`. 152 | * Can be a percentage (`10%,10%`), a number of pixels (`20,20`), or a mix (`30%,40`). 153 | * A negative number indicates that number of pixels away from the right/bottom instead of 154 | from the top/left; `-1,-1` would be the bottom right pixel. 155 | * Default: `50%,50%`. 156 | * ``: Optional. The size of the image on the screen, as `x`. 157 | * Can be a percentage (`10%x10%`), a number of pixels (`20x20`), or a mix (`30%x40`). 158 | * If the width is `-1`, the width is figured out based on the height and aspect ratio. 159 | * If the height is `-1`, the height is figured out based on the width and aspect ratio. 160 | * Default: The size of the image file. 161 | * ``: Optional. Determine which point of the image is placed at ``. 162 | * Possible values: `center`, `north`, `south`, `west`, `east`, 163 | `northwest`, `northeast`, southwest`, `southeast`. 164 | * With a `` of `northwest`, `` gives the location of the top/left 165 | corner of the image; with `southeast`, `` controls the bottom/right corner, 166 | `center` controls the middle of the image, etc. 167 | * Default: `center` if no `` is given; otherwise, intelligently decide a gravity 168 | based on position (`10,10` -> northwest, `-10,10` -> northeast, etc). 169 | * ``: The path to an image file. 170 | 171 | This command requires swaylock-effects to be compiled with gdk-pixbuf2. 172 | It supports all image formats gdk-pixbuf2 supports; on my system, that's 173 | png, jpeg, gif, svg, bmp, ico, tiff, wmf, ani, icns, pnm, qtif, tga, xbm and xpm. 174 | 175 | ### Custom 176 | 177 | `--effect-custom `: Load a custom effect from a shared object. 178 | 179 | The .so must export a function `void swaylock_effect(uint32_t *data, int width, int height)` 180 | or a function `uint32_t swaylock_pixel(uint32_t pix, int x, int y, int width, int height)`. 181 | -------------------------------------------------------------------------------- /background-image.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "background-image.h" 3 | #include "cairo.h" 4 | #include "log.h" 5 | #include "swaylock.h" 6 | 7 | // Cairo RGB24 uses 32 bits per pixel, as XRGB, in native endianness. 8 | // xrgb32_le uses 32 bits per pixel, as XRGB, little endian (BGRX big endian). 9 | void cairo_rgb24_from_xrgb32_le(unsigned char *buf, int width, int height, int stride) { 10 | for (int y = 0; y < height; ++y) { 11 | for (int x = 0; x < width; ++x) { 12 | unsigned char *pix = buf + y * stride + x * 4; 13 | *(uint32_t *)pix = 0 | 14 | (uint32_t)pix[2] << 16 | 15 | (uint32_t)pix[1] << 8 | 16 | (uint32_t)pix[0]; 17 | } 18 | } 19 | } 20 | 21 | // Cairo RGB24 uses 32 bits per pixel, as XRGB, in native endianness. 22 | // xbgr32_le uses 32 bits per pixel, as XBGR, little endian (RGBX big endian). 23 | void cairo_rgb24_from_xbgr32_le(unsigned char *buf, int width, int height, int stride) { 24 | for (int y = 0; y < height; ++y) { 25 | for (int x = 0; x < width; ++x) { 26 | unsigned char *pix = buf + y * stride + x * 4; 27 | *(uint32_t *)pix = 0 | 28 | (uint32_t)pix[0] << 16 | 29 | (uint32_t)pix[1] << 8 | 30 | (uint32_t)pix[2]; 31 | } 32 | } 33 | } 34 | 35 | enum background_mode parse_background_mode(const char *mode) { 36 | if (strcmp(mode, "stretch") == 0) { 37 | return BACKGROUND_MODE_STRETCH; 38 | } else if (strcmp(mode, "fill") == 0) { 39 | return BACKGROUND_MODE_FILL; 40 | } else if (strcmp(mode, "fit") == 0) { 41 | return BACKGROUND_MODE_FIT; 42 | } else if (strcmp(mode, "center") == 0) { 43 | return BACKGROUND_MODE_CENTER; 44 | } else if (strcmp(mode, "tile") == 0) { 45 | return BACKGROUND_MODE_TILE; 46 | } else if (strcmp(mode, "solid_color") == 0) { 47 | return BACKGROUND_MODE_SOLID_COLOR; 48 | } 49 | swaylock_log(LOG_ERROR, "Unsupported background mode: %s", mode); 50 | return BACKGROUND_MODE_INVALID; 51 | } 52 | 53 | cairo_surface_t *load_background_from_buffer(void *buf, uint32_t format, 54 | uint32_t width, uint32_t height, uint32_t stride, enum wl_output_transform transform) { 55 | bool rotated = 56 | transform == WL_OUTPUT_TRANSFORM_90 || 57 | transform == WL_OUTPUT_TRANSFORM_270 || 58 | transform == WL_OUTPUT_TRANSFORM_FLIPPED_90 || 59 | transform == WL_OUTPUT_TRANSFORM_FLIPPED_270; 60 | 61 | cairo_surface_t *image; 62 | if (rotated) { 63 | image = cairo_image_surface_create(CAIRO_FORMAT_RGB24, height, width); 64 | } else { 65 | image = cairo_image_surface_create(CAIRO_FORMAT_RGB24, width, height); 66 | } 67 | if (image == NULL) { 68 | swaylock_log(LOG_ERROR, "Failed to create image.."); 69 | return NULL; 70 | } 71 | 72 | unsigned char *destbuf = cairo_image_surface_get_data(image); 73 | size_t destwidth = cairo_image_surface_get_width(image); 74 | size_t destheight = cairo_image_surface_get_height(image); 75 | size_t deststride = cairo_image_surface_get_stride(image); 76 | unsigned char *srcbuf = buf; 77 | size_t srcstride = stride; 78 | size_t minstride = srcstride < deststride ? srcstride : deststride; 79 | 80 | // Lots of these are mostly-copy-and-pasted, with a lot of boilerplate 81 | // for each case. 82 | // The only interesting differencess between a lot of these cases are 83 | // the definitions of srcx and srcy. 84 | // I don't think it's worth adding a macro to make this "cleaner" though, 85 | // as that would obfuscate what's actually going on. 86 | switch (transform) { 87 | case WL_OUTPUT_TRANSFORM_NORMAL: 88 | // In most cases, the transform is probably normal. Luckily, it can be 89 | // done with just one big memcpy. 90 | if (srcstride == deststride) { 91 | memcpy(destbuf, srcbuf, destheight * deststride); 92 | } else { 93 | for (size_t y = 0; y < destheight; ++y) { 94 | memcpy(destbuf + y * deststride, srcbuf + y * srcstride, minstride); 95 | } 96 | } 97 | break; 98 | case WL_OUTPUT_TRANSFORM_90: 99 | for (size_t desty = 0; desty < destheight; ++desty) { 100 | size_t srcx = desty; 101 | for (size_t destx = 0; destx < destwidth; ++destx) { 102 | size_t srcy = destwidth - destx - 1; 103 | *((uint32_t *)(destbuf + desty * deststride) + destx) = 104 | *((uint32_t *)(srcbuf + srcy * srcstride) + srcx); 105 | } 106 | } 107 | break; 108 | case WL_OUTPUT_TRANSFORM_180: 109 | for (size_t desty = 0; desty < destheight; ++desty) { 110 | size_t srcy = destheight - desty - 1; 111 | for (size_t destx = 0; destx < destwidth; ++destx) { 112 | size_t srcx = destwidth - destx - 1; 113 | *((uint32_t *)(destbuf + desty * deststride) + destx) = 114 | *((uint32_t *)(srcbuf + srcy * srcstride) + srcx); 115 | } 116 | } 117 | break; 118 | case WL_OUTPUT_TRANSFORM_270: 119 | for (size_t desty = 0; desty < destheight; ++desty) { 120 | size_t srcx = destheight - desty - 1; 121 | for (size_t destx = 0; destx < destwidth; ++destx) { 122 | size_t srcy = destx; 123 | *((uint32_t *)(destbuf + desty * deststride) + destx) = 124 | *((uint32_t *)(srcbuf + srcy * srcstride) + srcx); 125 | } 126 | } 127 | break; 128 | case WL_OUTPUT_TRANSFORM_FLIPPED: 129 | for (size_t desty = 0; desty < destheight; ++desty) { 130 | size_t srcy = desty; 131 | for (size_t destx = 0; destx < destwidth; ++destx) { 132 | size_t srcx = destwidth - destx - 1; 133 | *((uint32_t *)(destbuf + desty * deststride) + destx) = 134 | *((uint32_t *)(srcbuf + srcy * srcstride) + srcx); 135 | } 136 | } 137 | break; 138 | case WL_OUTPUT_TRANSFORM_FLIPPED_90: 139 | for (size_t desty = 0; desty < destheight; ++desty) { 140 | size_t srcx = desty; 141 | for (size_t destx = 0; destx < destwidth; ++destx) { 142 | size_t srcy = destx; 143 | *((uint32_t *)(destbuf + desty * deststride) + destx) = 144 | *((uint32_t *)(srcbuf + srcy * srcstride) + srcx); 145 | } 146 | } 147 | break; 148 | case WL_OUTPUT_TRANSFORM_FLIPPED_180: 149 | for (size_t desty = 0; desty < destheight; ++desty) { 150 | size_t srcy = destheight - desty - 1; 151 | memcpy(destbuf + desty * deststride, srcbuf + srcy * srcstride, minstride); 152 | } 153 | break; 154 | case WL_OUTPUT_TRANSFORM_FLIPPED_270: 155 | for (size_t desty = 0; desty < destheight; ++desty) { 156 | size_t srcx = destheight - desty - 1; 157 | for (size_t destx = 0; destx < destwidth; ++destx) { 158 | size_t srcy = destwidth - destx - 1; 159 | *((uint32_t *)(destbuf + desty * deststride) + destx) = 160 | *((uint32_t *)(srcbuf + srcy * srcstride) + srcx); 161 | } 162 | } 163 | break; 164 | } 165 | 166 | if (format == WL_SHM_FORMAT_XBGR8888 || format == WL_SHM_FORMAT_ABGR8888) { 167 | cairo_rgb24_from_xbgr32_le( 168 | cairo_image_surface_get_data(image), 169 | cairo_image_surface_get_width(image), 170 | cairo_image_surface_get_height(image), 171 | cairo_image_surface_get_stride(image)); 172 | } else { 173 | if (format != WL_SHM_FORMAT_XRGB8888 && format != WL_SHM_FORMAT_ARGB8888) { 174 | swaylock_log(LOG_ERROR, 175 | "Unknown pixel format: %u. Assuming XRGB32. Colors may look wrong.", 176 | format); 177 | } 178 | 179 | // If we're little endian, we don't have to do anything 180 | int test = 1; 181 | bool is_little_endian = *(char *)&test == 1; 182 | if (!is_little_endian) { 183 | cairo_rgb24_from_xrgb32_le( 184 | cairo_image_surface_get_data(image), 185 | cairo_image_surface_get_width(image), 186 | cairo_image_surface_get_height(image), 187 | cairo_image_surface_get_stride(image)); 188 | } 189 | } 190 | 191 | return image; 192 | } 193 | 194 | cairo_surface_t *load_background_image(const char *path) { 195 | cairo_surface_t *image; 196 | #if HAVE_GDK_PIXBUF 197 | GError *err = NULL; 198 | GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(path, &err); 199 | if (!pixbuf) { 200 | swaylock_log(LOG_ERROR, "Failed to load background image (%s).", 201 | err->message); 202 | return NULL; 203 | } 204 | image = gdk_cairo_image_surface_create_from_pixbuf(pixbuf); 205 | g_object_unref(pixbuf); 206 | #else 207 | image = cairo_image_surface_create_from_png(path); 208 | #endif // HAVE_GDK_PIXBUF 209 | if (!image) { 210 | swaylock_log(LOG_ERROR, "Failed to read background image."); 211 | return NULL; 212 | } 213 | if (cairo_surface_status(image) != CAIRO_STATUS_SUCCESS) { 214 | swaylock_log(LOG_ERROR, "Failed to read background image: %s." 215 | #if !HAVE_GDK_PIXBUF 216 | "\nSway was compiled without gdk_pixbuf support, so only" 217 | "\nPNG images can be loaded. This is the likely cause." 218 | #endif // !HAVE_GDK_PIXBUF 219 | , cairo_status_to_string(cairo_surface_status(image))); 220 | return NULL; 221 | } 222 | return image; 223 | } 224 | 225 | void render_background_image(cairo_t *cairo, cairo_surface_t *image, 226 | enum background_mode mode, int buffer_width, int buffer_height) { 227 | double width = cairo_image_surface_get_width(image); 228 | double height = cairo_image_surface_get_height(image); 229 | 230 | cairo_save(cairo); 231 | switch (mode) { 232 | case BACKGROUND_MODE_STRETCH: 233 | cairo_scale(cairo, 234 | (double)buffer_width / width, 235 | (double)buffer_height / height); 236 | cairo_set_source_surface(cairo, image, 0, 0); 237 | break; 238 | case BACKGROUND_MODE_FILL: { 239 | double window_ratio = (double)buffer_width / buffer_height; 240 | double bg_ratio = width / height; 241 | 242 | if (window_ratio > bg_ratio) { 243 | double scale = (double)buffer_width / width; 244 | cairo_scale(cairo, scale, scale); 245 | cairo_set_source_surface(cairo, image, 246 | 0, (double)buffer_height / 2 / scale - height / 2); 247 | } else { 248 | double scale = (double)buffer_height / height; 249 | cairo_scale(cairo, scale, scale); 250 | cairo_set_source_surface(cairo, image, 251 | (double)buffer_width / 2 / scale - width / 2, 0); 252 | } 253 | break; 254 | } 255 | case BACKGROUND_MODE_FIT: { 256 | double window_ratio = (double)buffer_width / buffer_height; 257 | double bg_ratio = width / height; 258 | 259 | if (window_ratio > bg_ratio) { 260 | double scale = (double)buffer_height / height; 261 | cairo_scale(cairo, scale, scale); 262 | cairo_set_source_surface(cairo, image, 263 | (double)buffer_width / 2 / scale - width / 2, 0); 264 | } else { 265 | double scale = (double)buffer_width / width; 266 | cairo_scale(cairo, scale, scale); 267 | cairo_set_source_surface(cairo, image, 268 | 0, (double)buffer_height / 2 / scale - height / 2); 269 | } 270 | break; 271 | } 272 | case BACKGROUND_MODE_CENTER: 273 | cairo_set_source_surface(cairo, image, 274 | (double)buffer_width / 2 - width / 2, 275 | (double)buffer_height / 2 - height / 2); 276 | break; 277 | case BACKGROUND_MODE_TILE: { 278 | cairo_pattern_t *pattern = cairo_pattern_create_for_surface(image); 279 | cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT); 280 | cairo_set_source(cairo, pattern); 281 | break; 282 | } 283 | case BACKGROUND_MODE_SOLID_COLOR: 284 | case BACKGROUND_MODE_INVALID: 285 | assert(0); 286 | break; 287 | } 288 | cairo_paint(cairo); 289 | cairo_restore(cairo); 290 | } 291 | -------------------------------------------------------------------------------- /cairo.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "cairo.h" 4 | #if HAVE_GDK_PIXBUF 5 | #include 6 | #endif 7 | 8 | void cairo_set_source_u32(cairo_t *cairo, uint32_t color) { 9 | cairo_set_source_rgba(cairo, 10 | (color >> (3*8) & 0xFF) / 255.0, 11 | (color >> (2*8) & 0xFF) / 255.0, 12 | (color >> (1*8) & 0xFF) / 255.0, 13 | (color >> (0*8) & 0xFF) / 255.0); 14 | } 15 | 16 | cairo_subpixel_order_t to_cairo_subpixel_order(enum wl_output_subpixel subpixel) { 17 | switch (subpixel) { 18 | case WL_OUTPUT_SUBPIXEL_HORIZONTAL_RGB: 19 | return CAIRO_SUBPIXEL_ORDER_RGB; 20 | case WL_OUTPUT_SUBPIXEL_HORIZONTAL_BGR: 21 | return CAIRO_SUBPIXEL_ORDER_BGR; 22 | case WL_OUTPUT_SUBPIXEL_VERTICAL_RGB: 23 | return CAIRO_SUBPIXEL_ORDER_VRGB; 24 | case WL_OUTPUT_SUBPIXEL_VERTICAL_BGR: 25 | return CAIRO_SUBPIXEL_ORDER_VBGR; 26 | default: 27 | return CAIRO_SUBPIXEL_ORDER_DEFAULT; 28 | } 29 | return CAIRO_SUBPIXEL_ORDER_DEFAULT; 30 | } 31 | 32 | #if HAVE_GDK_PIXBUF 33 | cairo_surface_t* gdk_cairo_image_surface_create_from_pixbuf(const GdkPixbuf *gdkbuf) { 34 | int chan = gdk_pixbuf_get_n_channels(gdkbuf); 35 | if (chan < 3) { 36 | return NULL; 37 | } 38 | 39 | const guint8* gdkpix = gdk_pixbuf_read_pixels(gdkbuf); 40 | if (!gdkpix) { 41 | return NULL; 42 | } 43 | gint w = gdk_pixbuf_get_width(gdkbuf); 44 | gint h = gdk_pixbuf_get_height(gdkbuf); 45 | int stride = gdk_pixbuf_get_rowstride(gdkbuf); 46 | 47 | cairo_format_t fmt = (chan == 3) ? CAIRO_FORMAT_RGB24 : CAIRO_FORMAT_ARGB32; 48 | cairo_surface_t * cs = cairo_image_surface_create (fmt, w, h); 49 | cairo_surface_flush (cs); 50 | if ( !cs || cairo_surface_status(cs) != CAIRO_STATUS_SUCCESS) { 51 | return NULL; 52 | } 53 | 54 | int cstride = cairo_image_surface_get_stride(cs); 55 | unsigned char * cpix = cairo_image_surface_get_data(cs); 56 | 57 | if (chan == 3) { 58 | int i; 59 | for (i = h; i; --i) { 60 | const guint8 *gp = gdkpix; 61 | unsigned char *cp = cpix; 62 | const guint8* end = gp + 3*w; 63 | while (gp < end) { 64 | #if G_BYTE_ORDER == G_LITTLE_ENDIAN 65 | cp[0] = gp[2]; 66 | cp[1] = gp[1]; 67 | cp[2] = gp[0]; 68 | #else 69 | cp[1] = gp[0]; 70 | cp[2] = gp[1]; 71 | cp[3] = gp[2]; 72 | #endif 73 | gp += 3; 74 | cp += 4; 75 | } 76 | gdkpix += stride; 77 | cpix += cstride; 78 | } 79 | } else { 80 | /* premul-color = alpha/255 * color/255 * 255 = (alpha*color)/255 81 | * (z/255) = z/256 * 256/255 = z/256 (1 + 1/255) 82 | * = z/256 + (z/256)/255 = (z + z/255)/256 83 | * # recurse once 84 | * = (z + (z + z/255)/256)/256 85 | * = (z + z/256 + z/256/255) / 256 86 | * # only use 16bit uint operations, loose some precision, 87 | * # result is floored. 88 | * -> (z + z>>8)>>8 89 | * # add 0x80/255 = 0.5 to convert floor to round 90 | * => (z+0x80 + (z+0x80)>>8 ) >> 8 91 | * ------ 92 | * tested as equal to lround(z/255.0) for uint z in [0..0xfe02] 93 | */ 94 | #define PREMUL_ALPHA(x,a,b,z) \ 95 | G_STMT_START { z = a * b + 0x80; x = (z + (z >> 8)) >> 8; } \ 96 | G_STMT_END 97 | int i; 98 | for (i = h; i; --i) { 99 | const guint8 *gp = gdkpix; 100 | unsigned char *cp = cpix; 101 | const guint8* end = gp + 4*w; 102 | guint z1, z2, z3; 103 | while (gp < end) { 104 | #if G_BYTE_ORDER == G_LITTLE_ENDIAN 105 | PREMUL_ALPHA(cp[0], gp[2], gp[3], z1); 106 | PREMUL_ALPHA(cp[1], gp[1], gp[3], z2); 107 | PREMUL_ALPHA(cp[2], gp[0], gp[3], z3); 108 | cp[3] = gp[3]; 109 | #else 110 | PREMUL_ALPHA(cp[1], gp[0], gp[3], z1); 111 | PREMUL_ALPHA(cp[2], gp[1], gp[3], z2); 112 | PREMUL_ALPHA(cp[3], gp[2], gp[3], z3); 113 | cp[0] = gp[3]; 114 | #endif 115 | gp += 4; 116 | cp += 4; 117 | } 118 | gdkpix += stride; 119 | cpix += cstride; 120 | } 121 | #undef PREMUL_ALPHA 122 | } 123 | cairo_surface_mark_dirty(cs); 124 | return cs; 125 | } 126 | #endif // HAVE_GDK_PIXBUF 127 | -------------------------------------------------------------------------------- /comm.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "comm.h" 6 | #include "log.h" 7 | #include "swaylock.h" 8 | 9 | static int comm[2][2] = {{-1, -1}, {-1, -1}}; 10 | 11 | ssize_t read_comm_request(char **buf_ptr) { 12 | size_t size; 13 | ssize_t amt; 14 | amt = read(comm[0][0], &size, sizeof(size)); 15 | if (amt == 0) { 16 | return 0; 17 | } else if (amt < 0) { 18 | swaylock_log_errno(LOG_ERROR, "read pw request"); 19 | return -1; 20 | } 21 | swaylock_log(LOG_DEBUG, "received pw check request"); 22 | char *buf = malloc(size); 23 | if (!buf) { 24 | swaylock_log_errno(LOG_ERROR, "failed to malloc pw buffer"); 25 | return -1; 26 | } 27 | size_t offs = 0; 28 | do { 29 | amt = read(comm[0][0], &buf[offs], size - offs); 30 | if (amt <= 0) { 31 | swaylock_log_errno(LOG_ERROR, "failed to read pw"); 32 | return -1; 33 | } 34 | offs += (size_t)amt; 35 | } while (offs < size); 36 | 37 | *buf_ptr = buf; 38 | return size; 39 | } 40 | 41 | bool write_comm_reply(bool success) { 42 | if (write(comm[1][1], &success, sizeof(success)) != sizeof(success)) { 43 | swaylock_log_errno(LOG_ERROR, "failed to write pw check result"); 44 | return false; 45 | } 46 | return true; 47 | } 48 | 49 | bool spawn_comm_child(void) { 50 | if (pipe(comm[0]) != 0) { 51 | swaylock_log_errno(LOG_ERROR, "failed to create pipe"); 52 | return false; 53 | } 54 | if (pipe(comm[1]) != 0) { 55 | swaylock_log_errno(LOG_ERROR, "failed to create pipe"); 56 | return false; 57 | } 58 | pid_t child = fork(); 59 | if (child < 0) { 60 | swaylock_log_errno(LOG_ERROR, "failed to fork"); 61 | return false; 62 | } else if (child == 0) { 63 | close(comm[0][1]); 64 | close(comm[1][0]); 65 | run_pw_backend_child(); 66 | } 67 | close(comm[0][0]); 68 | close(comm[1][1]); 69 | return true; 70 | } 71 | 72 | bool write_comm_request(struct swaylock_password *pw) { 73 | bool result = false; 74 | 75 | size_t len = pw->len + 1; 76 | size_t offs = 0; 77 | if (write(comm[0][1], &len, sizeof(len)) < 0) { 78 | swaylock_log_errno(LOG_ERROR, "Failed to request pw check"); 79 | goto out; 80 | } 81 | 82 | do { 83 | ssize_t amt = write(comm[0][1], &pw->buffer[offs], len - offs); 84 | if (amt < 0) { 85 | swaylock_log_errno(LOG_ERROR, "Failed to write pw buffer"); 86 | goto out; 87 | } 88 | offs += amt; 89 | } while (offs < len); 90 | 91 | result = true; 92 | 93 | out: 94 | clear_password_buffer(pw); 95 | return result; 96 | } 97 | 98 | bool read_comm_reply(void) { 99 | bool result = false; 100 | if (read(comm[1][0], &result, sizeof(result)) != sizeof(result)) { 101 | swaylock_log_errno(LOG_ERROR, "Failed to read pw result"); 102 | result = false; 103 | } 104 | return result; 105 | } 106 | 107 | int get_comm_reply_fd(void) { 108 | return comm[1][0]; 109 | } 110 | -------------------------------------------------------------------------------- /completions/bash/swaylock: -------------------------------------------------------------------------------- 1 | # swaylock(1) completion 2 | 3 | _swaylock() 4 | { 5 | local cur prev 6 | _get_comp_words_by_ref -n : cur prev 7 | 8 | short=( 9 | -C 10 | -c 11 | -d 12 | -e 13 | -f 14 | -F 15 | -h 16 | -i 17 | -k 18 | -K 19 | -L 20 | -l 21 | -n 22 | -r 23 | -s 24 | -t 25 | -u 26 | -v 27 | ) 28 | 29 | long=( 30 | --bs-hl-color 31 | --caps-lock-bs-hl-color 32 | --caps-lock-key-hl-color 33 | --color 34 | --config 35 | --daemonize 36 | --debug 37 | --disable-caps-lock-text 38 | --font 39 | --font-size 40 | --help 41 | --hide-keyboard-layout 42 | --ignore-empty-password 43 | --image 44 | --indicator-caps-lock 45 | --indicator-idle-visible 46 | --indicator-radius 47 | --indicator-thickness 48 | --indicator-x-position 49 | --indicator-y-position 50 | --inside-caps-lock-color 51 | --inside-clear-color 52 | --inside-color 53 | --inside-ver-color 54 | --inside-wrong-color 55 | --key-hl-color 56 | --layout-bg-color 57 | --layout-border-color 58 | --layout-text-color 59 | --line-caps-lock-color 60 | --line-clear-color 61 | --line-color 62 | --line-uses-inside 63 | --line-uses-ring 64 | --line-ver-color 65 | --line-wrong-color 66 | --no-unlock-indicator 67 | --ring-caps-lock-color 68 | --ring-clear-color 69 | --ring-color 70 | --ring-ver-color 71 | --ring-wrong-color 72 | --scaling 73 | --separator-color 74 | --show-failed-attempts 75 | --show-keyboard-layout 76 | --text-caps-lock-color 77 | --text-clear-color 78 | --text-color 79 | --text-ver-color 80 | --text-wrong-color 81 | --tiling 82 | --version 83 | ) 84 | 85 | scaling=( 86 | 'stretch' 87 | 'fill' 88 | 'fit' 89 | 'center' 90 | 'tile' 91 | 'solid_color' 92 | ) 93 | 94 | case $prev in 95 | -c|--color) 96 | return 97 | ;; 98 | --scaling) 99 | COMPREPLY=($(compgen -W "${scaling[*]}" -- "$cur")) 100 | return 101 | ;; 102 | -i|--image) 103 | if grep -q : <<< "$cur"; then 104 | output="${cur%%:*}:" 105 | cur="${cur#*:}" 106 | else 107 | output= 108 | fi 109 | COMPREPLY=($(compgen -f -- "$cur")) 110 | return 111 | ;; 112 | esac 113 | 114 | if [[ $cur == --* ]]; then 115 | COMPREPLY=($(compgen -W "${long[*]}" -- "$cur")) 116 | else 117 | COMPREPLY=($(compgen -W "${short[*]}" -- "$cur")) 118 | COMPREPLY+=($(compgen -W "${long[*]}" -- "$cur")) 119 | fi 120 | 121 | } && 122 | complete -F _swaylock swaylock 123 | -------------------------------------------------------------------------------- /completions/fish/swaylock.fish: -------------------------------------------------------------------------------- 1 | # swaylock(1) completion 2 | 3 | complete -c swaylock -l bs-hl-color --description "Sets the color of backspace highlight segments." 4 | complete -c swaylock -l caps-lock-bs-hl-color --description "Sets the color of backspace highlight segments when Caps Lock is active." 5 | complete -c swaylock -l caps-lock-key-hl-color --description "Sets the color of the key press highlight segments when Caps Lock is active." 6 | complete -c swaylock -l color -s c --description "Turn the screen into the given color instead of white." 7 | complete -c swaylock -l config -s C --description "Path to the config file." 8 | complete -c swaylock -l daemonize -s f --description "Detach from the controlling terminal after locking." 9 | complete -c swaylock -l debug -s d --description "Enable debugging output." 10 | complete -c swaylock -l disable-caps-lock-text -s L --description "Disable the Caps Lock text." 11 | complete -c swaylock -l font --description "Sets the font of the text." 12 | complete -c swaylock -l font-size --description "Sets a fixed font size for the indicator text." 13 | complete -c swaylock -l help -s h --description "Show help message and quit." 14 | complete -c swaylock -l hide-keyboard-layout -s K --description "Hide the current xkb layout while typing." 15 | complete -c swaylock -l ignore-empty-password -s e --description "When an empty password is provided, do not validate it." 16 | complete -c swaylock -l image -s i --description "Display the given image, optionally only on the given output." 17 | complete -c swaylock -l indicator-caps-lock -s l --description "Show the current Caps Lock state also on the indicator." 18 | complete -c swaylock -l indicator-idle-visible --description "Sets the indicator to show even if idle." 19 | complete -c swaylock -l indicator-radius --description "Sets the indicator radius." 20 | complete -c swaylock -l indicator-thickness --description "Sets the indicator thickness." 21 | complete -c swaylock -l indicator-x-position --description "Sets the horizontal position of the indicator." 22 | complete -c swaylock -l indicator-y-position --description "Sets the vertical position of the indicator." 23 | complete -c swaylock -l inside-caps-lock-color --description "Sets the color of the inside of the indicator when Caps Lock is active." 24 | complete -c swaylock -l inside-clear-color --description "Sets the color of the inside of the indicator when cleared." 25 | complete -c swaylock -l inside-color --description "Sets the color of the inside of the indicator." 26 | complete -c swaylock -l inside-ver-color --description "Sets the color of the inside of the indicator when verifying." 27 | complete -c swaylock -l inside-wrong-color --description "Sets the color of the inside of the indicator when invalid." 28 | complete -c swaylock -l key-hl-color --description "Sets the color of the key press highlight segments." 29 | complete -c swaylock -l layout-bg-color --description "Sets the background color of the box containing the layout text." 30 | complete -c swaylock -l layout-border-color --description "Sets the color of the border of the box containing the layout text." 31 | complete -c swaylock -l layout-text-color --description "Sets the color of the layout text." 32 | complete -c swaylock -l line-caps-lock-color --description "Sets the color of the line between the inside and ring when Caps Lock is active." 33 | complete -c swaylock -l line-clear-color --description "Sets the color of the line between the inside and ring when cleared." 34 | complete -c swaylock -l line-color --description "Sets the color of the line between the inside and ring." 35 | complete -c swaylock -l line-uses-inside -s n --description "Use the inside color for the line between the inside and ring." 36 | complete -c swaylock -l line-uses-ring -s r --description "Use the ring color for the line between the inside and ring." 37 | complete -c swaylock -l line-ver-color --description "Sets the color of the line between the inside and ring when verifying." 38 | complete -c swaylock -l line-wrong-color --description "Sets the color of the line between the inside and ring when invalid." 39 | complete -c swaylock -l no-unlock-indicator -s u --description "Disable the unlock indicator." 40 | complete -c swaylock -l ring-caps-lock-color --description "Sets the color of the ring of the indicator when Caps Lock is active." 41 | complete -c swaylock -l ring-clear-color --description "Sets the color of the ring of the indicator when cleared." 42 | complete -c swaylock -l ring-color --description "Sets the color of the ring of the indicator." 43 | complete -c swaylock -l ring-ver-color --description "Sets the color of the ring of the indicator when verifying." 44 | complete -c swaylock -l ring-wrong-color --description "Sets the color of the ring of the indicator when invalid." 45 | complete -c swaylock -l scaling -s s --description "Image scaling mode: stretch, fill, fit, center, tile, solid_color." 46 | complete -c swaylock -l separator-color --description "Sets the color of the lines that separate highlight segments." 47 | complete -c swaylock -l show-failed-attempts -s F --description "Show current count of failed authentication attempts." 48 | complete -c swaylock -l show-keyboard-layout -s k --description "Display the current xkb layout while typing." 49 | complete -c swaylock -l text-caps-lock-color --description "Sets the color of the text when Caps Lock is active." 50 | complete -c swaylock -l text-clear-color --description "Sets the color of the text when cleared." 51 | complete -c swaylock -l text-color --description "Sets the color of the text." 52 | complete -c swaylock -l text-ver-color --description "Sets the color of the text when verifying." 53 | complete -c swaylock -l text-wrong-color --description "Sets the color of the text when invalid." 54 | complete -c swaylock -l tiling -s t --description "Same as --scaling=tile." 55 | complete -c swaylock -l version -s v --description "Show the version number and quit." 56 | -------------------------------------------------------------------------------- /completions/zsh/_swaylock: -------------------------------------------------------------------------------- 1 | #compdef swaylock 2 | # 3 | # Completion script for swaylock 4 | # 5 | 6 | _arguments -s \ 7 | '(--bs-hl-color)'--bs-hl-color'[Sets the color of backspace highlight segments]:color:' \ 8 | '(--caps-lock-bs-hl-color)'--caps-lock-bs-hl-color'[Sets the color of backspace highlight segments when Caps Lock is active]:color:' \ 9 | '(--caps-lock-key-hl-color)'--caps-lock-key-hl-color'[Sets the color of the key press highlight segments when Caps Lock is active]:color:' \ 10 | '(--color -c)'{--color,-c}'[Turn the screen into the given color instead of white]:color:' \ 11 | '(--config -C)'{--config,-C}'[Path to the config file]:filename:_files' \ 12 | '(--daemonize -f)'{--daemonize,-f}'[Detach from the controlling terminal after locking]' \ 13 | '(--debug -d)'{--debug,-d}'[Enable debugging output]' \ 14 | '(--disable-caps-lock-text -L)'{--disable-caps-lock-text,-L}'[Disable the Caps Lock text]' \ 15 | '(--font)'--font'[Sets the font of the text]:font:' \ 16 | '(--font-size)'--font-size'[Sets a fixed font size for the indicator text]' \ 17 | '(--help -h)'{--help,-h}'[Show help message and quit]' \ 18 | '(--hide-keyboard-layout -K)'{--hide-keyboard-layout,-K}'[Hide the current xkb layout while typing]' \ 19 | '(--ignore-empty-password -e)'{--ignore-empty-password,-e}'[When an empty password is provided, do not validate it]' \ 20 | '(--image -i)'{--image,-i}'[Display the given image, optionally only on the given output]:filename:_files' \ 21 | '(--indicator-caps-lock -l)'{--indicator-caps-lock,-l}'[Show the current Caps Lock state also on the indicator]' \ 22 | '(--indicator-idle-visible)'--indicator-idle-visible'[Sets the indicator to show even if idle]' \ 23 | '(--indicator-radius)'--indicator-radius'[Sets the indicator radius]:radius:' \ 24 | '(--indicator-thickness)'--indicator-thickness'[Sets the indicator thickness]:thickness:' \ 25 | '(--indicator-x-position)'--indicator-x-position'[Sets the horizontal position of the indicator]' \ 26 | '(--indicator-y-position)'--indicator-y-position'[Sets the vertical position of the indicator]' \ 27 | '(--inside-caps-lock-color)'--inside-caps-lock-color'[Sets the color of the inside of the indicator when Caps Lock is active]:color:' \ 28 | '(--inside-clear-color)'--inside-clear-color'[Sets the color of the inside of the indicator when cleared]:color:' \ 29 | '(--inside-color)'--inside-color'[Sets the color of the inside of the indicator]:color:' \ 30 | '(--inside-ver-color)'--inside-ver-color'[Sets the color of the inside of the indicator when verifying]:color:' \ 31 | '(--inside-wrong-color)'--inside-wrong-color'[Sets the color of the inside of the indicator when invalid]:color:' \ 32 | '(--key-hl-color)'--key-hl-color'[Sets the color of the key press highlight segments]:color:' \ 33 | '(--layout-bg-color)'--layout-bg-color'[Sets the background color of the box containing the layout text]:color:' \ 34 | '(--layout-border-color)'--layout-border-color'[Sets the color of the border of the box containing the layout text]:color:' \ 35 | '(--layout-text-color)'--layout-text-color'[Sets the color of the layout text]:color:' \ 36 | '(--line-caps-lock-color)'--line-caps-lock-color'[Sets the color of the line between the inside and ring when Caps Lock is active]:color:' \ 37 | '(--line-clear-color)'--line-clear-color'[Sets the color of the line between the inside and ring when cleared]:color:' \ 38 | '(--line-color)'--line-color'[Sets the color of the line between the inside and ring]:color:' \ 39 | '(--line-uses-inside -n)'{--line-uses-inside,-n}'[Use the inside color for the line between the inside and ring]' \ 40 | '(--line-uses-ring -r)'{--line-uses-ring,-r}'[Use the ring color for the line between the inside and ring]' \ 41 | '(--line-ver-color)'--line-ver-color'[Sets the color of the line between the inside and ring when verifying]:color:' \ 42 | '(--line-wrong-color)'--line-wrong-color'[Sets the color of the line between the inside and ring when invalid]:color:' \ 43 | '(--no-unlock-indicator -u)'{--no-unlock-indicator,-u}'[Disable the unlock indicator]' \ 44 | '(--ring-caps-lock-color)'--ring-caps-lock-color'[Sets the color of the ring of the indicator when Caps Lock is active]:color:' \ 45 | '(--ring-clear-color)'--ring-clear-color'[Sets the color of the ring of the indicator when cleared]:color:' \ 46 | '(--ring-color)'--ring-color'[Sets the color of the ring of the indicator]:color:' \ 47 | '(--ring-ver-color)'--ring-ver-color'[Sets the color of the ring of the indicator when verifying]:color:' \ 48 | '(--ring-wrong-color)'--ring-wrong-color'[Sets the color of the ring of the indicator when invalid]:color:' \ 49 | '(--scaling -s)'{--scaling,-s}'[Image scaling mode: stretch, fill, fit, center, tile, solid_color]:mode:(stretch fill fit center tile solid_color)' \ 50 | '(--separator-color)'--separator-color'[Sets the color of the lines that separate highlight segments]:color:' \ 51 | '(--show-failed-attempts -F)'{--show-failed-attempts,-F}'[Show current count of failed authentication attempts]' \ 52 | '(--show-keyboard-layout -k)'{--show-keyboard-layout,-k}'[Display the current xkb layout while typing]' \ 53 | '(--text-caps-lock-color)'--text-caps-lock-color'[Sets the color of the text when Caps Lock is active]:color:' \ 54 | '(--text-clear-color)'--text-clear-color'[Sets the color of the text when cleared]:color:' \ 55 | '(--text-color)'--text-color'[Sets the color of the text]:color:' \ 56 | '(--text-ver-color)'--text-ver-color'[Sets the color of the text when verifying]:color:' \ 57 | '(--text-wrong-color)'--text-wrong-color'[Sets the color of the text when invalid]:color:' \ 58 | '(--tiling -t)'{--tiling,-t}'[Same as --scaling=tile]' \ 59 | '(--version -v)'{--version,-v}'[Show the version number and quit]' 60 | -------------------------------------------------------------------------------- /effects.c: -------------------------------------------------------------------------------- 1 | #define _POSIX_C_SOURCE 200809 2 | #define _XOPEN_SOURCE 700 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 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 | 26 | extern char **environ; 27 | 28 | static 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 | 38 | static 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 | 53 | static 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 | 67 | static 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 | 116 | static 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 | 131 | static 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 | 178 | static 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 | 225 | static 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 234 | static 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 | 254 | static 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 | 291 | static 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 | 309 | static 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 | 326 | static 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 | 353 | static 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 | 424 | static 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 | 459 | static 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 | 482 | static 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 | 560 | static 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 | 578 | static 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 | 688 | static 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 | 714 | cairo_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 | 730 | cairo_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 | } 758 | -------------------------------------------------------------------------------- /fade.c: -------------------------------------------------------------------------------- 1 | #include "fade.h" 2 | #include "pool-buffer.h" 3 | #include "swaylock.h" 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #ifdef FADE_PROFILE 11 | #include 12 | double get_time() { 13 | struct timespec tv; 14 | clock_gettime(CLOCK_MONOTONIC, &tv); 15 | return tv.tv_sec + (tv.tv_nsec / 1000000000.0); 16 | } 17 | #endif 18 | 19 | #if defined(USE_SSE) && defined(__SSE2__) 20 | #define set_alpha set_alpha_sse 21 | 22 | #include 23 | 24 | static void set_alpha_sse(uint32_t *orig, struct pool_buffer *buf, float alpha) { 25 | int alpha_factor = (int)(alpha * (1 << 16)); 26 | if (alpha_factor != 0) 27 | alpha_factor -= 1; 28 | 29 | __m128i alpha_vec = _mm_set_epi16( 30 | alpha_factor, alpha_factor, alpha_factor, alpha_factor, 31 | alpha_factor, alpha_factor, alpha_factor, alpha_factor); 32 | __m128i dummy_vec = _mm_set_epi16(0, 0, 0, 0, 0, 0, 0, 0); 33 | 34 | uint8_t *orig_bytes = (uint8_t *)orig; 35 | uint8_t *dest_bytes = (uint8_t *)buf->data; 36 | size_t length = ((size_t)buf->width * (size_t)buf->height * 4) / 8; 37 | 38 | for (size_t i = 0; i < length; ++i) { 39 | size_t index = i * 8; 40 | 41 | // Read data into SSE register, where each byte is an u16 42 | __m128i argb_vec = _mm_loadu_si64(orig_bytes + index); 43 | argb_vec = _mm_unpacklo_epi8(argb_vec, dummy_vec); 44 | 45 | // Multiply the 8 argb u16s with the 8 alpha u16s 46 | argb_vec = _mm_mulhi_epu16(argb_vec, alpha_vec); 47 | 48 | // Put the low bytes of each argb u16 into the destination buffer 49 | argb_vec = _mm_packus_epi16(argb_vec, dummy_vec); 50 | _mm_storeu_si64(dest_bytes + index, argb_vec); 51 | } 52 | } 53 | 54 | #else 55 | #define set_alpha set_alpha_slow 56 | 57 | static void set_alpha_slow(uint32_t *orig, struct pool_buffer *buf, float alpha) { 58 | for (size_t y = 0; y < buf->height; ++y) { 59 | for (size_t x = 0; x < buf->width; ++x) { 60 | size_t index = y * buf->width + x; 61 | uint32_t srcpix = orig[index]; 62 | int srcr = (srcpix & 0x00ff0000u) >> 16; 63 | int srcg = (srcpix & 0x0000ff00u) >> 8; 64 | int srcb = (srcpix & 0x000000ffu); 65 | 66 | ((uint32_t *)buf->data)[index] = 0 | 67 | (uint32_t)(alpha * 255) << 24 | 68 | (uint32_t)(srcr * alpha) << 16 | 69 | (uint32_t)(srcg * alpha) << 8 | 70 | (uint32_t)(srcb * alpha); 71 | } 72 | } 73 | } 74 | 75 | #endif 76 | 77 | void fade_prepare(struct swaylock_fade *fade, struct pool_buffer *buffer) { 78 | if (!fade->target_time) { 79 | fade->original_buffer = NULL; 80 | return; 81 | } 82 | 83 | size_t size = (size_t)buffer->width * (size_t)buffer->height * 4; 84 | fade->original_buffer = malloc(size); 85 | memcpy(fade->original_buffer, buffer->data, size); 86 | 87 | set_alpha(fade->original_buffer, buffer, 0); 88 | } 89 | 90 | void fade_update(struct swaylock_fade *fade, struct pool_buffer *buffer, uint32_t time) { 91 | if (fade->current_time >= fade->target_time) { 92 | return; 93 | } 94 | 95 | double delta = 0; 96 | if (fade->old_time != 0) { 97 | delta = time - fade->old_time; 98 | } 99 | fade->old_time = time; 100 | 101 | fade->current_time += delta; 102 | if (fade->current_time > fade->target_time) { 103 | fade->current_time = fade->target_time; 104 | } 105 | 106 | double alpha = (double)fade->current_time / (double)fade->target_time; 107 | 108 | #ifdef FADE_PROFILE 109 | double before = get_time(); 110 | #endif 111 | 112 | set_alpha(fade->original_buffer, buffer, alpha); 113 | 114 | #ifdef FADE_PROFILE 115 | double after = get_time(); 116 | printf("set alpha in %fms (%fFPS). %fms since last time, FPS: %f\n", 117 | (after - before) * 1000, 1 / (after - before), 118 | delta, 1000 / delta); 119 | #endif 120 | } 121 | 122 | bool fade_is_complete(struct swaylock_fade *fade) { 123 | return fade->target_time == 0 || fade->current_time >= fade->target_time; 124 | } 125 | 126 | void fade_destroy(struct swaylock_fade *fade) { 127 | free(fade->original_buffer); 128 | } 129 | -------------------------------------------------------------------------------- /include/background-image.h: -------------------------------------------------------------------------------- 1 | #ifndef _SWAY_BACKGROUND_IMAGE_H 2 | #define _SWAY_BACKGROUND_IMAGE_H 3 | #include 4 | #include "cairo.h" 5 | 6 | enum background_mode { 7 | BACKGROUND_MODE_STRETCH, 8 | BACKGROUND_MODE_FILL, 9 | BACKGROUND_MODE_FIT, 10 | BACKGROUND_MODE_CENTER, 11 | BACKGROUND_MODE_TILE, 12 | BACKGROUND_MODE_SOLID_COLOR, 13 | BACKGROUND_MODE_INVALID, 14 | }; 15 | 16 | struct swaylock_surface; 17 | 18 | enum background_mode parse_background_mode(const char *mode); 19 | cairo_surface_t *load_background_image(const char *path); 20 | cairo_surface_t *load_background_from_buffer(void *buf, uint32_t format, 21 | uint32_t width, uint32_t height, uint32_t stride, enum wl_output_transform transform); 22 | void render_background_image(cairo_t *cairo, cairo_surface_t *image, 23 | enum background_mode mode, int buffer_width, int buffer_height); 24 | 25 | #endif 26 | -------------------------------------------------------------------------------- /include/cairo.h: -------------------------------------------------------------------------------- 1 | #ifndef _SWAY_CAIRO_H 2 | #define _SWAY_CAIRO_H 3 | 4 | #include "config.h" 5 | #include 6 | #include 7 | #include 8 | #if HAVE_GDK_PIXBUF 9 | #include 10 | #endif 11 | 12 | void cairo_set_source_u32(cairo_t *cairo, uint32_t color); 13 | cairo_subpixel_order_t to_cairo_subpixel_order(enum wl_output_subpixel subpixel); 14 | 15 | cairo_surface_t *cairo_image_surface_scale(cairo_surface_t *image, 16 | int width, int height); 17 | 18 | #if HAVE_GDK_PIXBUF 19 | 20 | cairo_surface_t* gdk_cairo_image_surface_create_from_pixbuf( 21 | const GdkPixbuf *gdkbuf); 22 | 23 | #endif // HAVE_GDK_PIXBUF 24 | 25 | #endif 26 | -------------------------------------------------------------------------------- /include/comm.h: -------------------------------------------------------------------------------- 1 | #ifndef _SWAYLOCK_COMM_H 2 | #define _SWAYLOCK_COMM_H 3 | 4 | #include 5 | 6 | struct swaylock_password; 7 | 8 | bool spawn_comm_child(void); 9 | ssize_t read_comm_request(char **buf_ptr); 10 | bool write_comm_reply(bool success); 11 | // Requests the provided password to be checked. The password is always cleared 12 | // when the function returns. 13 | bool write_comm_request(struct swaylock_password *pw); 14 | bool read_comm_reply(void); 15 | // FD to poll for password authentication replies. 16 | int get_comm_reply_fd(void); 17 | 18 | #endif 19 | -------------------------------------------------------------------------------- /include/effects.h: -------------------------------------------------------------------------------- 1 | #ifndef _SWAYLOCK_EFFECTS_H 2 | #define _SWAYLOCK_EFFECTS_H 3 | 4 | #include 5 | 6 | #include "cairo.h" 7 | 8 | struct swaylock_effect_screen_pos { 9 | float pos; 10 | bool is_percent; 11 | }; 12 | 13 | struct swaylock_effect { 14 | union { 15 | struct { 16 | int radius, times; 17 | } blur; 18 | struct { 19 | int factor; 20 | } pixelate; 21 | double scale; 22 | struct { 23 | double base; 24 | double factor; 25 | } vignette; 26 | struct { 27 | struct swaylock_effect_screen_pos x; 28 | struct swaylock_effect_screen_pos y; 29 | struct swaylock_effect_screen_pos w; 30 | struct swaylock_effect_screen_pos h; 31 | enum { 32 | EFFECT_COMPOSE_GRAV_CENTER, 33 | EFFECT_COMPOSE_GRAV_NW, 34 | EFFECT_COMPOSE_GRAV_NE, 35 | EFFECT_COMPOSE_GRAV_SW, 36 | EFFECT_COMPOSE_GRAV_SE, 37 | EFFECT_COMPOSE_GRAV_N, 38 | EFFECT_COMPOSE_GRAV_S, 39 | EFFECT_COMPOSE_GRAV_E, 40 | EFFECT_COMPOSE_GRAV_W, 41 | } gravity; 42 | char *imgpath; 43 | } compose; 44 | char *custom; 45 | } e; 46 | 47 | enum { 48 | EFFECT_BLUR, 49 | EFFECT_PIXELATE, 50 | EFFECT_SCALE, 51 | EFFECT_GREYSCALE, 52 | EFFECT_VIGNETTE, 53 | EFFECT_COMPOSE, 54 | EFFECT_CUSTOM, 55 | } tag; 56 | }; 57 | 58 | cairo_surface_t *swaylock_effects_run(cairo_surface_t *surface, int scale, 59 | struct swaylock_effect *effects, int count); 60 | 61 | cairo_surface_t *swaylock_effects_run_timed(cairo_surface_t *surface, int scale, 62 | struct swaylock_effect *effects, int count); 63 | 64 | #endif 65 | -------------------------------------------------------------------------------- /include/fade.h: -------------------------------------------------------------------------------- 1 | #ifndef _SWAYLOCK_FADE_H 2 | #define _SWAYLOCK_FADE_H 3 | 4 | #include 5 | #include 6 | 7 | struct pool_buffer; 8 | 9 | struct swaylock_fade { 10 | float current_time; 11 | float target_time; 12 | uint32_t old_time; 13 | uint32_t *original_buffer; 14 | }; 15 | 16 | void fade_prepare(struct swaylock_fade *fade, struct pool_buffer *buffer); 17 | void fade_update(struct swaylock_fade *fade, struct pool_buffer *buffer, uint32_t time); 18 | bool fade_is_complete(struct swaylock_fade *fade); 19 | void fade_destroy(struct swaylock_fade *fade); 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /include/log.h: -------------------------------------------------------------------------------- 1 | #ifndef _SWAYLOCK_LOG_H 2 | #define _SWAYLOCK_LOG_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | enum log_importance { 9 | LOG_SILENT = 0, 10 | LOG_ERROR = 1, 11 | LOG_INFO = 2, 12 | LOG_DEBUG = 3, 13 | LOG_TRACE = 4, 14 | LOG_IMPORTANCE_LAST, 15 | }; 16 | 17 | void swaylock_log_init(enum log_importance verbosity); 18 | 19 | #ifdef __GNUC__ 20 | #define _ATTRIB_PRINTF(start, end) __attribute__((format(printf, start, end))) 21 | #else 22 | #define _ATTRIB_PRINTF(start, end) 23 | #endif 24 | 25 | void _swaylock_log(enum log_importance verbosity, const char *format, ...) 26 | _ATTRIB_PRINTF(2, 3); 27 | 28 | void _swaylock_trace(const char *file, int line, const char *func); 29 | 30 | const char *_swaylock_strip_path(const char *filepath); 31 | 32 | #define swaylock_log(verb, fmt, ...) \ 33 | _swaylock_log(verb, "[%s:%d] " fmt, _swaylock_strip_path(__FILE__), \ 34 | __LINE__, ##__VA_ARGS__) 35 | 36 | #define swaylock_log_errno(verb, fmt, ...) \ 37 | swaylock_log(verb, fmt ": %s", ##__VA_ARGS__, strerror(errno)) 38 | 39 | #define swaylock_trace() \ 40 | _swaylock_trace(__FILE__, __LINE__, __func__) 41 | 42 | #endif 43 | -------------------------------------------------------------------------------- /include/loop.h: -------------------------------------------------------------------------------- 1 | #ifndef _SWAY_LOOP_H 2 | #define _SWAY_LOOP_H 3 | #include 4 | 5 | /** 6 | * This is an event loop system designed for sway clients, not sway itself. 7 | * 8 | * The loop consists of file descriptors and timers. Typically the Wayland 9 | * display's file descriptor will be one of the fds in the loop. 10 | */ 11 | 12 | struct loop; 13 | struct loop_timer; 14 | 15 | /** 16 | * Create an event loop. 17 | */ 18 | struct loop *loop_create(void); 19 | 20 | /** 21 | * Destroy the event loop (eg. on program termination). 22 | */ 23 | void loop_destroy(struct loop *loop); 24 | 25 | /** 26 | * Poll the event loop. This will block until one of the fds has data. 27 | */ 28 | void loop_poll(struct loop *loop); 29 | 30 | /** 31 | * Add a file descriptor to the loop. 32 | */ 33 | void loop_add_fd(struct loop *loop, int fd, short mask, 34 | void (*func)(int fd, short mask, void *data), void *data); 35 | 36 | /** 37 | * Add a timer to the loop. 38 | * 39 | * When the timer expires, the timer will be removed from the loop and freed. 40 | */ 41 | struct loop_timer *loop_add_timer(struct loop *loop, int ms, 42 | void (*callback)(void *data), void *data); 43 | 44 | /** 45 | * Remove a file descriptor from the loop. 46 | */ 47 | bool loop_remove_fd(struct loop *loop, int fd); 48 | 49 | /** 50 | * Remove a timer from the loop. 51 | */ 52 | bool loop_remove_timer(struct loop *loop, struct loop_timer *timer); 53 | 54 | #endif 55 | -------------------------------------------------------------------------------- /include/meson.build: -------------------------------------------------------------------------------- 1 | configure_file(output: 'config.h', configuration: conf_data) 2 | -------------------------------------------------------------------------------- /include/pool-buffer.h: -------------------------------------------------------------------------------- 1 | #ifndef _SWAY_BUFFERS_H 2 | #define _SWAY_BUFFERS_H 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | struct pool_buffer { 9 | struct wl_buffer *buffer; 10 | cairo_surface_t *surface; 11 | cairo_t *cairo; 12 | uint32_t width, height; 13 | void *data; 14 | size_t size; 15 | bool busy; 16 | }; 17 | 18 | struct pool_buffer *get_next_buffer(struct wl_shm *shm, 19 | struct pool_buffer pool[static 2], uint32_t width, uint32_t height); 20 | void destroy_buffer(struct pool_buffer *buffer); 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /include/seat.h: -------------------------------------------------------------------------------- 1 | #ifndef _SWAYLOCK_SEAT_H 2 | #define _SWAYLOCK_SEAT_H 3 | #include 4 | #include 5 | #include 6 | 7 | struct loop; 8 | struct loop_timer; 9 | 10 | struct swaylock_xkb { 11 | bool caps_lock; 12 | bool control; 13 | struct xkb_state *state; 14 | struct xkb_context *context; 15 | struct xkb_keymap *keymap; 16 | }; 17 | 18 | struct swaylock_seat { 19 | struct swaylock_state *state; 20 | struct wl_pointer *pointer; 21 | struct wl_keyboard *keyboard; 22 | struct wl_touch *touch; 23 | int32_t repeat_period_ms; 24 | int32_t repeat_delay_ms; 25 | uint32_t repeat_sym; 26 | uint32_t repeat_codepoint; 27 | struct loop_timer *repeat_timer; 28 | }; 29 | 30 | extern const struct wl_seat_listener seat_listener; 31 | 32 | #endif 33 | -------------------------------------------------------------------------------- /include/swaylock.h: -------------------------------------------------------------------------------- 1 | #ifndef _SWAYLOCK_H 2 | #define _SWAYLOCK_H 3 | #include 4 | #include 5 | #include 6 | #include "background-image.h" 7 | #include "cairo.h" 8 | #include "pool-buffer.h" 9 | #include "seat.h" 10 | #include "effects.h" 11 | #include "fade.h" 12 | #include "wlr-layer-shell-unstable-v1-client-protocol.h" 13 | 14 | enum auth_state { 15 | AUTH_STATE_IDLE, 16 | AUTH_STATE_CLEAR, 17 | AUTH_STATE_INPUT, 18 | AUTH_STATE_INPUT_NOP, 19 | AUTH_STATE_BACKSPACE, 20 | AUTH_STATE_VALIDATING, 21 | AUTH_STATE_INVALID, 22 | AUTH_STATE_GRACE, 23 | }; 24 | 25 | struct swaylock_colorset { 26 | uint32_t input; 27 | uint32_t cleared; 28 | uint32_t caps_lock; 29 | uint32_t verifying; 30 | uint32_t wrong; 31 | }; 32 | 33 | struct swaylock_colors { 34 | uint32_t background; 35 | uint32_t bs_highlight; 36 | uint32_t key_highlight; 37 | uint32_t caps_lock_bs_highlight; 38 | uint32_t caps_lock_key_highlight; 39 | uint32_t separator; 40 | uint32_t layout_background; 41 | uint32_t layout_border; 42 | uint32_t layout_text; 43 | struct swaylock_colorset inside; 44 | struct swaylock_colorset line; 45 | struct swaylock_colorset ring; 46 | struct swaylock_colorset text; 47 | }; 48 | 49 | struct swaylock_args { 50 | struct swaylock_colors colors; 51 | enum background_mode mode; 52 | char *font; 53 | uint32_t font_size; 54 | uint32_t radius; 55 | uint32_t thickness; 56 | uint32_t indicator_x_position; 57 | uint32_t indicator_y_position; 58 | bool override_indicator_x_position; 59 | bool override_indicator_y_position; 60 | bool ignore_empty; 61 | bool show_indicator; 62 | bool show_caps_lock_text; 63 | bool show_caps_lock_indicator; 64 | bool show_keyboard_layout; 65 | bool hide_keyboard_layout; 66 | bool show_failed_attempts; 67 | bool daemonize; 68 | bool indicator_idle_visible; 69 | 70 | bool screenshots; 71 | struct swaylock_effect *effects; 72 | int effects_count; 73 | bool time_effects; 74 | bool indicator; 75 | bool clock; 76 | char *timestr; 77 | char *datestr; 78 | uint32_t fade_in; 79 | bool password_submit_on_touch; 80 | uint32_t password_grace_period; 81 | bool password_grace_no_mouse; 82 | bool password_grace_no_touch; 83 | }; 84 | 85 | struct swaylock_password { 86 | size_t len; 87 | char buffer[1024]; 88 | }; 89 | 90 | struct swaylock_state { 91 | struct loop *eventloop; 92 | struct loop_timer *clear_indicator_timer; // clears the indicator 93 | struct loop_timer *clear_password_timer; // clears the password buffer 94 | struct wl_display *display; 95 | struct wl_compositor *compositor; 96 | struct wl_subcompositor *subcompositor; 97 | struct zwlr_layer_shell_v1 *layer_shell; 98 | struct zwlr_input_inhibit_manager_v1 *input_inhibit_manager; 99 | struct zwlr_screencopy_manager_v1 *screencopy_manager; 100 | struct wl_shm *shm; 101 | struct wl_list surfaces; 102 | struct wl_list images; 103 | struct swaylock_args args; 104 | struct swaylock_password password; 105 | struct swaylock_xkb xkb; 106 | enum auth_state auth_state; 107 | bool indicator_dirty; 108 | int render_randnum; 109 | int failed_attempts; 110 | size_t n_screenshots_done; 111 | bool run_display; 112 | struct zxdg_output_manager_v1 *zxdg_output_manager; 113 | }; 114 | 115 | struct swaylock_surface { 116 | cairo_surface_t *image; 117 | struct { 118 | uint32_t format, width, height, stride; 119 | enum wl_output_transform transform; 120 | void *data; 121 | struct swaylock_image *image; 122 | } screencopy; 123 | struct swaylock_state *state; 124 | struct wl_output *output; 125 | uint32_t output_global_name; 126 | struct zxdg_output_v1 *xdg_output; 127 | struct wl_surface *surface; 128 | struct wl_surface *child; // surface made into subsurface 129 | struct wl_subsurface *subsurface; 130 | struct zwlr_layer_surface_v1 *layer_surface; 131 | struct zwlr_screencopy_frame_v1 *screencopy_frame; 132 | struct pool_buffer buffers[2]; 133 | struct pool_buffer indicator_buffers[2]; 134 | struct pool_buffer *current_buffer; 135 | struct swaylock_fade fade; 136 | int events_pending; 137 | bool configured; 138 | bool frame_pending, dirty; 139 | uint32_t width, height; 140 | uint32_t indicator_width, indicator_height; 141 | int32_t scale; 142 | enum wl_output_subpixel subpixel; 143 | enum wl_output_transform transform; 144 | char *output_name; 145 | struct wl_list link; 146 | }; 147 | 148 | // There is exactly one swaylock_image for each -i argument 149 | struct swaylock_image { 150 | char *path; 151 | char *output_name; 152 | cairo_surface_t *cairo_surface; 153 | struct wl_list link; 154 | }; 155 | 156 | void swaylock_handle_key(struct swaylock_state *state, 157 | xkb_keysym_t keysym, uint32_t codepoint); 158 | void swaylock_handle_mouse(struct swaylock_state *state); 159 | void swaylock_handle_touch(struct swaylock_state *state); 160 | void render_frame_background(struct swaylock_surface *surface); 161 | void render_background_fade(struct swaylock_surface *surface, uint32_t time); 162 | void render_background_fade_prepare(struct swaylock_surface *surface, struct pool_buffer *buffer); 163 | void render_frame(struct swaylock_surface *surface); 164 | void render_frames(struct swaylock_state *state); 165 | void damage_surface(struct swaylock_surface *surface); 166 | void damage_state(struct swaylock_state *state); 167 | void clear_password_buffer(struct swaylock_password *pw); 168 | void schedule_indicator_clear(struct swaylock_state *state); 169 | 170 | void initialize_pw_backend(int argc, char **argv); 171 | void run_pw_backend_child(void); 172 | void clear_buffer(char *buf, size_t size); 173 | 174 | #endif 175 | -------------------------------------------------------------------------------- /include/unicode.h: -------------------------------------------------------------------------------- 1 | #ifndef _SWAY_UNICODE_H 2 | #define _SWAY_UNICODE_H 3 | #include 4 | #include 5 | 6 | // Technically UTF-8 supports up to 6 byte codepoints, but Unicode itself 7 | // doesn't really bother with more than 4. 8 | #define UTF8_MAX_SIZE 4 9 | 10 | #define UTF8_INVALID 0x80 11 | 12 | /** 13 | * Grabs the next UTF-8 character and advances the string pointer 14 | */ 15 | uint32_t utf8_decode(const char **str); 16 | 17 | /** 18 | * Encodes a character as UTF-8 and returns the length of that character. 19 | */ 20 | size_t utf8_encode(char *str, uint32_t ch); 21 | 22 | /** 23 | * Returns the size of the next UTF-8 character 24 | */ 25 | int utf8_size(const char *str); 26 | 27 | /** 28 | * Returns the size of a UTF-8 character 29 | */ 30 | size_t utf8_chsize(uint32_t ch); 31 | 32 | #endif 33 | 34 | -------------------------------------------------------------------------------- /log.c: -------------------------------------------------------------------------------- 1 | #define _POSIX_C_SOURCE 199506L 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "log.h" 10 | 11 | static enum log_importance log_importance = LOG_ERROR; 12 | 13 | static const char *verbosity_colors[] = { 14 | [LOG_SILENT] = "", 15 | [LOG_ERROR ] = "\x1B[1;31m", 16 | [LOG_INFO ] = "\x1B[1;34m", 17 | [LOG_DEBUG ] = "\x1B[1;30m", 18 | [LOG_TRACE ] = "\x1B[1;32m", 19 | }; 20 | 21 | void swaylock_log_init(enum log_importance verbosity) { 22 | if (verbosity < LOG_IMPORTANCE_LAST) { 23 | log_importance = verbosity; 24 | } 25 | } 26 | 27 | void _swaylock_log(enum log_importance verbosity, const char *fmt, ...) { 28 | if (verbosity > log_importance) { 29 | return; 30 | } 31 | 32 | va_list args; 33 | va_start(args, fmt); 34 | 35 | // prefix the time to the log message 36 | struct tm result; 37 | time_t t = time(NULL); 38 | struct tm *tm_info = localtime_r(&t, &result); 39 | char buffer[26]; 40 | 41 | // generate time prefix 42 | strftime(buffer, sizeof(buffer), "%F %T - ", tm_info); 43 | fprintf(stderr, "%s", buffer); 44 | 45 | unsigned c = (verbosity < LOG_IMPORTANCE_LAST) 46 | ? verbosity : LOG_IMPORTANCE_LAST - 1; 47 | 48 | if (isatty(STDERR_FILENO)) { 49 | fprintf(stderr, "%s", verbosity_colors[c]); 50 | } 51 | 52 | vfprintf(stderr, fmt, args); 53 | 54 | if (isatty(STDERR_FILENO)) { 55 | fprintf(stderr, "\x1B[0m"); 56 | } 57 | fprintf(stderr, "\n"); 58 | 59 | va_end(args); 60 | } 61 | 62 | // This is mainly here for performance. 63 | // Don't want to do _swaylock_strip_path every event if we're not tracing. 64 | void _swaylock_trace(const char *file, int line, const char *func) { 65 | if (LOG_TRACE > log_importance) { 66 | return; 67 | } 68 | 69 | _swaylock_log(LOG_TRACE, "[%s:%d]: trace: %s", 70 | _swaylock_strip_path(file), line, func); 71 | } 72 | 73 | const char *_swaylock_strip_path(const char *filepath) { 74 | if (*filepath == '.') { 75 | while (*filepath == '.' || *filepath == '/') { 76 | ++filepath; 77 | } 78 | } 79 | return filepath; 80 | } 81 | -------------------------------------------------------------------------------- /loop.c: -------------------------------------------------------------------------------- 1 | #define _POSIX_C_SOURCE 200809L 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include "log.h" 12 | #include "loop.h" 13 | 14 | struct loop_fd_event { 15 | void (*callback)(int fd, short mask, void *data); 16 | void *data; 17 | struct wl_list link; // struct loop_fd_event::link 18 | }; 19 | 20 | struct loop_timer { 21 | void (*callback)(void *data); 22 | void *data; 23 | struct timespec expiry; 24 | bool removed; 25 | struct wl_list link; // struct loop_timer::link 26 | }; 27 | 28 | struct loop { 29 | struct pollfd *fds; 30 | int fd_length; 31 | int fd_capacity; 32 | 33 | struct wl_list fd_events; // struct loop_fd_event::link 34 | struct wl_list timers; // struct loop_timer::link 35 | }; 36 | 37 | struct loop *loop_create(void) { 38 | struct loop *loop = calloc(1, sizeof(struct loop)); 39 | if (!loop) { 40 | swaylock_log(LOG_ERROR, "Unable to allocate memory for loop"); 41 | return NULL; 42 | } 43 | loop->fd_capacity = 10; 44 | loop->fds = malloc(sizeof(struct pollfd) * loop->fd_capacity); 45 | wl_list_init(&loop->fd_events); 46 | wl_list_init(&loop->timers); 47 | return loop; 48 | } 49 | 50 | void loop_destroy(struct loop *loop) { 51 | struct loop_fd_event *event = NULL, *tmp_event = NULL; 52 | wl_list_for_each_safe(event, tmp_event, &loop->fd_events, link) { 53 | wl_list_remove(&event->link); 54 | free(event); 55 | } 56 | struct loop_timer *timer = NULL, *tmp_timer = NULL; 57 | wl_list_for_each_safe(timer, tmp_timer, &loop->timers, link) { 58 | wl_list_remove(&timer->link); 59 | free(timer); 60 | } 61 | free(loop->fds); 62 | free(loop); 63 | } 64 | 65 | void loop_poll(struct loop *loop) { 66 | // Calculate next timer in ms 67 | int ms = INT_MAX; 68 | if (!wl_list_empty(&loop->timers)) { 69 | struct timespec now; 70 | clock_gettime(CLOCK_MONOTONIC, &now); 71 | struct loop_timer *timer = NULL; 72 | wl_list_for_each(timer, &loop->timers, link) { 73 | int timer_ms = (timer->expiry.tv_sec - now.tv_sec) * 1000; 74 | timer_ms += (timer->expiry.tv_nsec - now.tv_nsec) / 1000000; 75 | if (timer_ms < ms) { 76 | ms = timer_ms; 77 | } 78 | } 79 | } 80 | if (ms < 0) { 81 | ms = 0; 82 | } 83 | 84 | int ret = poll(loop->fds, loop->fd_length, ms); 85 | if (ret < 0) { 86 | swaylock_log_errno(LOG_ERROR, "poll failed"); 87 | exit(1); 88 | } 89 | 90 | // Dispatch fds 91 | size_t fd_index = 0; 92 | struct loop_fd_event *event = NULL; 93 | wl_list_for_each(event, &loop->fd_events, link) { 94 | struct pollfd pfd = loop->fds[fd_index]; 95 | 96 | // Always send these events 97 | unsigned events = pfd.events | POLLHUP | POLLERR; 98 | 99 | if (pfd.revents & events) { 100 | event->callback(pfd.fd, pfd.revents, event->data); 101 | } 102 | 103 | ++fd_index; 104 | } 105 | 106 | // Dispatch timers 107 | if (!wl_list_empty(&loop->timers)) { 108 | struct timespec now; 109 | clock_gettime(CLOCK_MONOTONIC, &now); 110 | struct loop_timer *timer = NULL, *tmp_timer = NULL; 111 | wl_list_for_each_safe(timer, tmp_timer, &loop->timers, link) { 112 | if (timer->removed) { 113 | wl_list_remove(&timer->link); 114 | free(timer); 115 | continue; 116 | } 117 | 118 | bool expired = timer->expiry.tv_sec < now.tv_sec || 119 | (timer->expiry.tv_sec == now.tv_sec && 120 | timer->expiry.tv_nsec < now.tv_nsec); 121 | if (expired) { 122 | timer->callback(timer->data); 123 | wl_list_remove(&timer->link); 124 | free(timer); 125 | } 126 | } 127 | } 128 | } 129 | 130 | void loop_add_fd(struct loop *loop, int fd, short mask, 131 | void (*callback)(int fd, short mask, void *data), void *data) { 132 | struct loop_fd_event *event = calloc(1, sizeof(struct loop_fd_event)); 133 | if (!event) { 134 | swaylock_log(LOG_ERROR, "Unable to allocate memory for event"); 135 | return; 136 | } 137 | event->callback = callback; 138 | event->data = data; 139 | wl_list_insert(loop->fd_events.prev, &event->link); 140 | 141 | struct pollfd pfd = {fd, mask, 0}; 142 | 143 | if (loop->fd_length == loop->fd_capacity) { 144 | loop->fd_capacity += 10; 145 | loop->fds = realloc(loop->fds, 146 | sizeof(struct pollfd) * loop->fd_capacity); 147 | } 148 | 149 | loop->fds[loop->fd_length++] = pfd; 150 | } 151 | 152 | struct loop_timer *loop_add_timer(struct loop *loop, int ms, 153 | void (*callback)(void *data), void *data) { 154 | struct loop_timer *timer = calloc(1, sizeof(struct loop_timer)); 155 | if (!timer) { 156 | swaylock_log(LOG_ERROR, "Unable to allocate memory for timer"); 157 | return NULL; 158 | } 159 | timer->callback = callback; 160 | timer->data = data; 161 | 162 | clock_gettime(CLOCK_MONOTONIC, &timer->expiry); 163 | timer->expiry.tv_sec += ms / 1000; 164 | 165 | long int nsec = (ms % 1000) * 1000000; 166 | if (timer->expiry.tv_nsec + nsec >= 1000000000) { 167 | timer->expiry.tv_sec++; 168 | nsec -= 1000000000; 169 | } 170 | timer->expiry.tv_nsec += nsec; 171 | 172 | wl_list_insert(&loop->timers, &timer->link); 173 | 174 | return timer; 175 | } 176 | 177 | bool loop_remove_fd(struct loop *loop, int fd) { 178 | size_t fd_index = 0; 179 | struct loop_fd_event *event = NULL, *tmp_event = NULL; 180 | wl_list_for_each_safe(event, tmp_event, &loop->fd_events, link) { 181 | if (loop->fds[fd_index].fd == fd) { 182 | wl_list_remove(&event->link); 183 | free(event); 184 | 185 | loop->fd_length--; 186 | memmove(&loop->fds[fd_index], &loop->fds[fd_index + 1], 187 | sizeof(struct pollfd) * (loop->fd_length - fd_index)); 188 | return true; 189 | } 190 | ++fd_index; 191 | } 192 | return false; 193 | } 194 | 195 | bool loop_remove_timer(struct loop *loop, struct loop_timer *remove) { 196 | struct loop_timer *timer = NULL, *tmp_timer = NULL; 197 | wl_list_for_each_safe(timer, tmp_timer, &loop->timers, link) { 198 | if (timer == remove) { 199 | timer->removed = true; 200 | return true; 201 | } 202 | } 203 | return false; 204 | } 205 | -------------------------------------------------------------------------------- /meson.build: -------------------------------------------------------------------------------- 1 | project( 2 | 'swaylock', 3 | 'c', 4 | version: '1.6-4', 5 | license: 'MIT', 6 | meson_version: '>=0.48.0', 7 | default_options: [ 8 | 'c_std=c11', 9 | 'warning_level=2', 10 | 'werror=true', 11 | ], 12 | ) 13 | 14 | add_project_arguments( 15 | [ 16 | '-Wno-unused-parameter', 17 | '-Wno-unused-result', 18 | '-Wundef', 19 | '-Wvla', 20 | '-fopenmp', 21 | ], 22 | language: 'c', 23 | ) 24 | 25 | add_project_link_arguments( 26 | [ 27 | '-fopenmp', 28 | ], 29 | language: 'c', 30 | ) 31 | 32 | cc = meson.get_compiler('c') 33 | 34 | sysconfdir = get_option('sysconfdir') 35 | prefix = get_option('prefix') 36 | is_freebsd = host_machine.system().startswith('freebsd') 37 | 38 | add_project_arguments( 39 | '-DSYSCONFDIR="/@0@"'.format(join_paths(prefix, sysconfdir)), 40 | language : 'c') 41 | 42 | if is_freebsd 43 | add_project_arguments('-D_C11_SOURCE', language: 'c') 44 | endif 45 | 46 | if get_option('sse') 47 | add_project_arguments('-DUSE_SSE', language: 'c') 48 | endif 49 | 50 | test_cflags = [ 51 | '-mtune=native', 52 | ] 53 | foreach cflag : test_cflags 54 | if cc.has_argument(cflag) 55 | add_project_arguments(cflag, language: 'c') 56 | endif 57 | endforeach 58 | 59 | wayland_client = dependency('wayland-client') 60 | wayland_protos = dependency('wayland-protocols', version: '>=1.14') 61 | xkbcommon = dependency('xkbcommon') 62 | cairo = dependency('cairo') 63 | gdk_pixbuf = dependency('gdk-pixbuf-2.0', required: get_option('gdk-pixbuf')) 64 | bash_comp = dependency('bash-completion', required: false) 65 | fish_comp = dependency('fish', required: false) 66 | libpam = cc.find_library('pam', required: get_option('pam')) 67 | crypt = cc.find_library('crypt', required: not libpam.found()) 68 | math = cc.find_library('m') 69 | rt = cc.find_library('rt') 70 | dl = cc.find_library('dl') 71 | 72 | git = find_program('git', required: false) 73 | scdoc = find_program('scdoc', required: get_option('man-pages')) 74 | wayland_scanner = find_program('wayland-scanner') 75 | 76 | version = '"@0@"'.format(meson.project_version()) 77 | if git.found() 78 | git_commit_hash = run_command([git.path(), 'describe', '--always', '--tags']) 79 | git_branch = run_command([git.path(), 'rev-parse', '--abbrev-ref', 'HEAD']) 80 | if git_commit_hash.returncode() == 0 and git_branch.returncode() == 0 81 | version = '"@0@ (" __DATE__ ", branch \'@1@\')"'.format(git_commit_hash.stdout().strip(), git_branch.stdout().strip()) 82 | endif 83 | endif 84 | add_project_arguments('-DSWAYLOCK_VERSION=@0@'.format(version), language: 'c') 85 | 86 | wl_protocol_dir = wayland_protos.get_pkgconfig_variable('pkgdatadir') 87 | 88 | if wayland_client.version().version_compare('>=1.14.91') 89 | code_type = 'private-code' 90 | else 91 | code_type = 'code' 92 | endif 93 | 94 | wayland_scanner_code = generator( 95 | wayland_scanner, 96 | output: '@BASENAME@-protocol.c', 97 | arguments: [code_type, '@INPUT@', '@OUTPUT@'], 98 | ) 99 | 100 | wayland_scanner_client = generator( 101 | wayland_scanner, 102 | output: '@BASENAME@-client-protocol.h', 103 | arguments: ['client-header', '@INPUT@', '@OUTPUT@'], 104 | ) 105 | 106 | client_protos_src = [] 107 | client_protos_headers = [] 108 | 109 | client_protocols = [ 110 | [wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'], 111 | [wl_protocol_dir, 'unstable/xdg-output/xdg-output-unstable-v1.xml'], 112 | ['wlr-layer-shell-unstable-v1.xml'], 113 | ['wlr-input-inhibitor-unstable-v1.xml'], 114 | ['wlr-screencopy-unstable-v1.xml'], 115 | ] 116 | 117 | foreach p : client_protocols 118 | xml = join_paths(p) 119 | client_protos_src += wayland_scanner_code.process(xml) 120 | client_protos_headers += wayland_scanner_client.process(xml) 121 | endforeach 122 | 123 | lib_client_protos = static_library( 124 | 'client_protos', 125 | client_protos_src + client_protos_headers, 126 | dependencies: [wayland_client] 127 | ) # for the include directory 128 | 129 | client_protos = declare_dependency( 130 | link_with: lib_client_protos, 131 | sources: client_protos_headers, 132 | ) 133 | 134 | conf_data = configuration_data() 135 | conf_data.set10('HAVE_GDK_PIXBUF', gdk_pixbuf.found()) 136 | 137 | subdir('include') 138 | 139 | dependencies = [ 140 | cairo, 141 | client_protos, 142 | gdk_pixbuf, 143 | math, 144 | rt, 145 | dl, 146 | xkbcommon, 147 | wayland_client, 148 | ] 149 | 150 | sources = [ 151 | 'background-image.c', 152 | 'cairo.c', 153 | 'comm.c', 154 | 'log.c', 155 | 'loop.c', 156 | 'main.c', 157 | 'password.c', 158 | 'pool-buffer.c', 159 | 'render.c', 160 | 'seat.c', 161 | 'unicode.c', 162 | 'effects.c', 163 | 'fade.c', 164 | ] 165 | 166 | if libpam.found() 167 | sources += ['pam.c'] 168 | dependencies += [libpam] 169 | else 170 | warning('The swaylock binary must be setuid when compiled without libpam') 171 | warning('You must do this manually post-install: chmod a+s /path/to/swaylock') 172 | sources += ['shadow.c'] 173 | dependencies += [crypt] 174 | endif 175 | 176 | swaylock_inc = include_directories('include') 177 | 178 | executable('swaylock', 179 | sources, 180 | include_directories: [swaylock_inc], 181 | dependencies: dependencies, 182 | install: true 183 | ) 184 | 185 | install_data( 186 | 'pam/swaylock', 187 | install_dir: sysconfdir + '/pam.d/' 188 | ) 189 | 190 | if scdoc.found() 191 | sh = find_program('sh') 192 | mandir = get_option('mandir') 193 | man_files = [ 194 | 'swaylock.1.scd', 195 | ] 196 | foreach filename : man_files 197 | topic = filename.split('.')[-3].split('/')[-1] 198 | section = filename.split('.')[-2] 199 | output = '@0@.@1@'.format(topic, section) 200 | 201 | custom_target( 202 | output, 203 | input: filename, 204 | output: output, 205 | command: [ 206 | sh, '-c', '@0@ < @INPUT@ > @1@'.format(scdoc.path(), output) 207 | ], 208 | install: true, 209 | install_dir: '@0@/man@1@'.format(mandir, section) 210 | ) 211 | endforeach 212 | endif 213 | 214 | datadir = get_option('datadir') 215 | 216 | if get_option('zsh-completions') 217 | zsh_files = files( 218 | 'completions/zsh/_swaylock', 219 | ) 220 | zsh_install_dir = datadir + '/zsh/site-functions' 221 | 222 | install_data(zsh_files, install_dir: zsh_install_dir) 223 | endif 224 | 225 | if get_option('bash-completions') 226 | bash_files = files( 227 | 'completions/bash/swaylock', 228 | ) 229 | if bash_comp.found() 230 | bash_install_dir = bash_comp.get_pkgconfig_variable('completionsdir') 231 | else 232 | bash_install_dir = datadir + '/bash-completion/completions' 233 | endif 234 | 235 | install_data(bash_files, install_dir: bash_install_dir) 236 | endif 237 | 238 | if get_option('fish-completions') 239 | fish_files = files( 240 | 'completions/fish/swaylock.fish', 241 | ) 242 | if fish_comp.found() 243 | fish_install_dir = fish_comp.get_pkgconfig_variable('completionsdir') 244 | else 245 | fish_install_dir = datadir + '/fish/vendor_completions.d' 246 | endif 247 | 248 | install_data(fish_files, install_dir: fish_install_dir) 249 | endif 250 | -------------------------------------------------------------------------------- /meson_options.txt: -------------------------------------------------------------------------------- 1 | option('pam', type: 'feature', value: 'auto', description: 'Use PAM instead of shadow') 2 | option('gdk-pixbuf', type: 'feature', value: 'auto', description: 'Enable support for more image formats') 3 | option('man-pages', type: 'feature', value: 'auto', description: 'Generate and install man pages') 4 | option('zsh-completions', type: 'boolean', value: true, description: 'Install zsh shell completions') 5 | option('bash-completions', type: 'boolean', value: true, description: 'Install bash shell completions') 6 | option('fish-completions', type: 'boolean', value: true, description: 'Install fish shell completions') 7 | option('sse', type: 'boolean', value: true, description: 'Use SSE instructions where possible') 8 | -------------------------------------------------------------------------------- /pam.c: -------------------------------------------------------------------------------- 1 | #define _POSIX_C_SOURCE 200809L 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "comm.h" 9 | #include "log.h" 10 | #include "swaylock.h" 11 | 12 | static char *pw_buf = NULL; 13 | 14 | void initialize_pw_backend(int argc, char **argv) { 15 | if (getuid() != geteuid() || getgid() != getegid()) { 16 | swaylock_log(LOG_ERROR, 17 | "swaylock is setuid, but was compiled with the PAM" 18 | " backend. Run 'chmod a-s %s' to fix. Aborting.", argv[0]); 19 | exit(EXIT_FAILURE); 20 | } 21 | if (!spawn_comm_child()) { 22 | exit(EXIT_FAILURE); 23 | } 24 | } 25 | 26 | static int handle_conversation(int num_msg, const struct pam_message **msg, 27 | struct pam_response **resp, void *data) { 28 | /* PAM expects an array of responses, one for each message */ 29 | struct pam_response *pam_reply = 30 | calloc(num_msg, sizeof(struct pam_response)); 31 | if (pam_reply == NULL) { 32 | swaylock_log(LOG_ERROR, "Allocation failed"); 33 | return PAM_ABORT; 34 | } 35 | *resp = pam_reply; 36 | for (int i = 0; i < num_msg; ++i) { 37 | switch (msg[i]->msg_style) { 38 | case PAM_PROMPT_ECHO_OFF: 39 | case PAM_PROMPT_ECHO_ON: 40 | pam_reply[i].resp = strdup(pw_buf); // PAM clears and frees this 41 | if (pam_reply[i].resp == NULL) { 42 | swaylock_log(LOG_ERROR, "Allocation failed"); 43 | return PAM_ABORT; 44 | } 45 | break; 46 | case PAM_ERROR_MSG: 47 | case PAM_TEXT_INFO: 48 | break; 49 | } 50 | } 51 | return PAM_SUCCESS; 52 | } 53 | 54 | static const char *get_pam_auth_error(int pam_status) { 55 | switch (pam_status) { 56 | case PAM_AUTH_ERR: 57 | return "invalid credentials"; 58 | case PAM_CRED_INSUFFICIENT: 59 | return "swaylock cannot authenticate users; check /etc/pam.d/swaylock " 60 | "has been installed properly"; 61 | case PAM_AUTHINFO_UNAVAIL: 62 | return "authentication information unavailable"; 63 | case PAM_MAXTRIES: 64 | return "maximum number of authentication tries exceeded"; 65 | default:; 66 | static char msg[64]; 67 | snprintf(msg, sizeof(msg), "unknown error (%d)", pam_status); 68 | return msg; 69 | } 70 | } 71 | 72 | void run_pw_backend_child(void) { 73 | struct passwd *passwd = getpwuid(getuid()); 74 | char *username = passwd->pw_name; 75 | 76 | const struct pam_conv conv = { 77 | .conv = handle_conversation, 78 | .appdata_ptr = NULL, 79 | }; 80 | pam_handle_t *auth_handle = NULL; 81 | if (pam_start("swaylock", username, &conv, &auth_handle) != PAM_SUCCESS) { 82 | swaylock_log(LOG_ERROR, "pam_start failed"); 83 | exit(EXIT_FAILURE); 84 | } 85 | 86 | /* This code does not run as root */ 87 | swaylock_log(LOG_DEBUG, "Prepared to authorize user %s", username); 88 | 89 | int pam_status = PAM_SUCCESS; 90 | while (1) { 91 | ssize_t size = read_comm_request(&pw_buf); 92 | if (size < 0) { 93 | exit(EXIT_FAILURE); 94 | } else if (size == 0) { 95 | break; 96 | } 97 | 98 | int pam_status = pam_authenticate(auth_handle, 0); 99 | bool success = pam_status == PAM_SUCCESS; 100 | if (!success) { 101 | swaylock_log(LOG_ERROR, "pam_authenticate failed: %s", 102 | get_pam_auth_error(pam_status)); 103 | } 104 | 105 | if (!write_comm_reply(success)) { 106 | clear_buffer(pw_buf, size); 107 | exit(EXIT_FAILURE); 108 | } 109 | 110 | clear_buffer(pw_buf, size); 111 | free(pw_buf); 112 | pw_buf = NULL; 113 | } 114 | 115 | pam_setcred(auth_handle, PAM_REFRESH_CRED); 116 | 117 | if (pam_end(auth_handle, pam_status) != PAM_SUCCESS) { 118 | swaylock_log(LOG_ERROR, "pam_end failed"); 119 | exit(EXIT_FAILURE); 120 | } 121 | 122 | exit((pam_status == PAM_SUCCESS) ? EXIT_SUCCESS : EXIT_FAILURE); 123 | } 124 | -------------------------------------------------------------------------------- /pam/swaylock: -------------------------------------------------------------------------------- 1 | # 2 | # PAM configuration file for the swaylock screen locker. By default, it includes 3 | # the 'login' configuration file (see /etc/pam.d/login) 4 | # 5 | 6 | auth include login 7 | -------------------------------------------------------------------------------- /password.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "comm.h" 9 | #include "log.h" 10 | #include "loop.h" 11 | #include "seat.h" 12 | #include "swaylock.h" 13 | #include "unicode.h" 14 | 15 | void clear_buffer(char *buf, size_t size) { 16 | // Use volatile keyword so so compiler can't optimize this out. 17 | volatile char *buffer = buf; 18 | volatile char zero = '\0'; 19 | for (size_t i = 0; i < size; ++i) { 20 | buffer[i] = zero; 21 | } 22 | } 23 | 24 | void clear_password_buffer(struct swaylock_password *pw) { 25 | clear_buffer(pw->buffer, sizeof(pw->buffer)); 26 | pw->len = 0; 27 | } 28 | 29 | static bool backspace(struct swaylock_password *pw) { 30 | if (pw->len != 0) { 31 | pw->buffer[--pw->len] = 0; 32 | return true; 33 | } 34 | return false; 35 | } 36 | 37 | static void append_ch(struct swaylock_password *pw, uint32_t codepoint) { 38 | size_t utf8_size = utf8_chsize(codepoint); 39 | if (pw->len + utf8_size + 1 >= sizeof(pw->buffer)) { 40 | // TODO: Display error 41 | return; 42 | } 43 | utf8_encode(&pw->buffer[pw->len], codepoint); 44 | pw->buffer[pw->len + utf8_size] = 0; 45 | pw->len += utf8_size; 46 | } 47 | 48 | static void clear_indicator(void *data) { 49 | struct swaylock_state *state = data; 50 | state->clear_indicator_timer = NULL; 51 | state->auth_state = AUTH_STATE_IDLE; 52 | damage_state(state); 53 | } 54 | 55 | void schedule_indicator_clear(struct swaylock_state *state) { 56 | if (state->clear_indicator_timer) { 57 | loop_remove_timer(state->eventloop, state->clear_indicator_timer); 58 | } 59 | state->clear_indicator_timer = loop_add_timer( 60 | state->eventloop, 3000, clear_indicator, state); 61 | } 62 | 63 | static void clear_password(void *data) { 64 | struct swaylock_state *state = data; 65 | state->clear_password_timer = NULL; 66 | state->auth_state = AUTH_STATE_CLEAR; 67 | clear_password_buffer(&state->password); 68 | damage_state(state); 69 | schedule_indicator_clear(state); 70 | } 71 | 72 | static void schedule_password_clear(struct swaylock_state *state) { 73 | if (state->clear_password_timer) { 74 | loop_remove_timer(state->eventloop, state->clear_password_timer); 75 | } 76 | state->clear_password_timer = loop_add_timer( 77 | state->eventloop, 10000, clear_password, state); 78 | } 79 | 80 | static void submit_password(struct swaylock_state *state) { 81 | if (state->args.ignore_empty && state->password.len == 0) { 82 | return; 83 | } 84 | 85 | state->auth_state = AUTH_STATE_VALIDATING; 86 | 87 | if (!write_comm_request(&state->password)) { 88 | state->auth_state = AUTH_STATE_INVALID; 89 | schedule_indicator_clear(state); 90 | } 91 | 92 | damage_state(state); 93 | } 94 | 95 | void swaylock_handle_mouse(struct swaylock_state *state) { 96 | if (state->auth_state == AUTH_STATE_GRACE && !state->args.password_grace_no_mouse) { 97 | state->run_display = false; 98 | } 99 | } 100 | 101 | void swaylock_handle_touch(struct swaylock_state *state) { 102 | if (state->auth_state == AUTH_STATE_GRACE && !state->args.password_grace_no_touch) { 103 | state->run_display = false; 104 | } else if (state->auth_state != AUTH_STATE_VALIDATING && state->args.password_submit_on_touch) { 105 | submit_password(state); 106 | } 107 | } 108 | 109 | void swaylock_handle_key(struct swaylock_state *state, 110 | xkb_keysym_t keysym, uint32_t codepoint) { 111 | // Authentication not needed 112 | if (state->auth_state == AUTH_STATE_GRACE) { 113 | state->run_display = false; 114 | return; 115 | } 116 | // Ignore input events if validating 117 | if (state->auth_state == AUTH_STATE_VALIDATING) { 118 | return; 119 | } 120 | 121 | switch (keysym) { 122 | case XKB_KEY_KP_Enter: /* fallthrough */ 123 | case XKB_KEY_Return: 124 | submit_password(state); 125 | break; 126 | case XKB_KEY_Delete: 127 | case XKB_KEY_BackSpace: 128 | if (backspace(&state->password)) { 129 | state->auth_state = AUTH_STATE_BACKSPACE; 130 | } else { 131 | state->auth_state = AUTH_STATE_CLEAR; 132 | } 133 | state->indicator_dirty = true; 134 | damage_state(state); 135 | schedule_indicator_clear(state); 136 | schedule_password_clear(state); 137 | break; 138 | case XKB_KEY_Escape: 139 | clear_password_buffer(&state->password); 140 | state->auth_state = AUTH_STATE_CLEAR; 141 | state->indicator_dirty = true; 142 | schedule_indicator_clear(state); 143 | break; 144 | case XKB_KEY_Caps_Lock: 145 | case XKB_KEY_Shift_L: 146 | case XKB_KEY_Shift_R: 147 | case XKB_KEY_Control_L: 148 | case XKB_KEY_Control_R: 149 | case XKB_KEY_Meta_L: 150 | case XKB_KEY_Meta_R: 151 | case XKB_KEY_Alt_L: 152 | case XKB_KEY_Alt_R: 153 | case XKB_KEY_Super_L: 154 | case XKB_KEY_Super_R: 155 | state->auth_state = AUTH_STATE_INPUT_NOP; 156 | damage_state(state); 157 | schedule_indicator_clear(state); 158 | schedule_password_clear(state); 159 | break; 160 | case XKB_KEY_m: /* fallthrough */ 161 | case XKB_KEY_d: 162 | case XKB_KEY_j: 163 | if (state->xkb.control) { 164 | submit_password(state); 165 | break; 166 | } 167 | // fallthrough 168 | case XKB_KEY_c: /* fallthrough */ 169 | case XKB_KEY_u: 170 | if (state->xkb.control) { 171 | clear_password_buffer(&state->password); 172 | state->auth_state = AUTH_STATE_CLEAR; 173 | damage_state(state); 174 | schedule_indicator_clear(state); 175 | break; 176 | } 177 | // fallthrough 178 | default: 179 | if (codepoint) { 180 | append_ch(&state->password, codepoint); 181 | state->auth_state = AUTH_STATE_INPUT; 182 | state->indicator_dirty = true; 183 | damage_state(state); 184 | schedule_indicator_clear(state); 185 | schedule_password_clear(state); 186 | } 187 | break; 188 | } 189 | } 190 | -------------------------------------------------------------------------------- /pool-buffer.c: -------------------------------------------------------------------------------- 1 | #define _POSIX_C_SOURCE 200809L 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include "pool-buffer.h" 12 | 13 | static bool set_cloexec(int fd) { 14 | long flags = fcntl(fd, F_GETFD); 15 | if (flags == -1) { 16 | return false; 17 | } 18 | 19 | if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1) { 20 | return false; 21 | } 22 | 23 | return true; 24 | } 25 | 26 | static int create_pool_file(size_t size, char **name) { 27 | static const char template[] = "sway-client-XXXXXX"; 28 | const char *path = getenv("XDG_RUNTIME_DIR"); 29 | if (path == NULL) { 30 | fprintf(stderr, "XDG_RUNTIME_DIR is not set\n"); 31 | return -1; 32 | } 33 | 34 | size_t name_size = strlen(template) + 1 + strlen(path) + 1; 35 | *name = malloc(name_size); 36 | if (*name == NULL) { 37 | fprintf(stderr, "allocation failed\n"); 38 | return -1; 39 | } 40 | snprintf(*name, name_size, "%s/%s", path, template); 41 | 42 | int fd = mkstemp(*name); 43 | if (fd < 0) { 44 | return -1; 45 | } 46 | 47 | if (!set_cloexec(fd)) { 48 | close(fd); 49 | return -1; 50 | } 51 | 52 | if (ftruncate(fd, size) < 0) { 53 | close(fd); 54 | return -1; 55 | } 56 | 57 | return fd; 58 | } 59 | 60 | static void buffer_release(void *data, struct wl_buffer *wl_buffer) { 61 | struct pool_buffer *buffer = data; 62 | buffer->busy = false; 63 | } 64 | 65 | static const struct wl_buffer_listener buffer_listener = { 66 | .release = buffer_release 67 | }; 68 | 69 | static struct pool_buffer *create_buffer(struct wl_shm *shm, 70 | struct pool_buffer *buf, int32_t width, int32_t height, 71 | uint32_t format) { 72 | uint32_t stride = width * 4; 73 | size_t size = stride * height; 74 | 75 | void *data = NULL; 76 | if (size > 0) { 77 | char *name; 78 | int fd = create_pool_file(size, &name); 79 | assert(fd != -1); 80 | data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); 81 | struct wl_shm_pool *pool = wl_shm_create_pool(shm, fd, size); 82 | buf->buffer = wl_shm_pool_create_buffer(pool, 0, 83 | width, height, stride, format); 84 | wl_buffer_add_listener(buf->buffer, &buffer_listener, buf); 85 | wl_shm_pool_destroy(pool); 86 | close(fd); 87 | unlink(name); 88 | free(name); 89 | fd = -1; 90 | } 91 | 92 | buf->size = size; 93 | buf->width = width; 94 | buf->height = height; 95 | buf->data = data; 96 | buf->surface = cairo_image_surface_create_for_data(data, 97 | CAIRO_FORMAT_ARGB32, width, height, stride); 98 | buf->cairo = cairo_create(buf->surface); 99 | return buf; 100 | } 101 | 102 | void destroy_buffer(struct pool_buffer *buffer) { 103 | if (buffer->buffer) { 104 | wl_buffer_destroy(buffer->buffer); 105 | } 106 | if (buffer->cairo) { 107 | cairo_destroy(buffer->cairo); 108 | } 109 | if (buffer->surface) { 110 | cairo_surface_destroy(buffer->surface); 111 | } 112 | if (buffer->data) { 113 | munmap(buffer->data, buffer->size); 114 | } 115 | memset(buffer, 0, sizeof(struct pool_buffer)); 116 | } 117 | 118 | struct pool_buffer *get_next_buffer(struct wl_shm *shm, 119 | struct pool_buffer pool[static 2], uint32_t width, uint32_t height) { 120 | struct pool_buffer *buffer = NULL; 121 | 122 | for (size_t i = 0; i < 2; ++i) { 123 | if (pool[i].busy) { 124 | continue; 125 | } 126 | buffer = &pool[i]; 127 | } 128 | 129 | if (!buffer) { 130 | return NULL; 131 | } 132 | 133 | if (buffer->width != width || buffer->height != height) { 134 | destroy_buffer(buffer); 135 | } 136 | 137 | if (!buffer->buffer) { 138 | if (!create_buffer(shm, buffer, width, height, 139 | WL_SHM_FORMAT_ARGB8888)) { 140 | return NULL; 141 | } 142 | } 143 | buffer->busy = true; 144 | return buffer; 145 | } 146 | -------------------------------------------------------------------------------- /render.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "cairo.h" 7 | #include "background-image.h" 8 | #include "swaylock.h" 9 | 10 | #define M_PI 3.14159265358979323846 11 | const float TYPE_INDICATOR_RANGE = M_PI / 3.0f; 12 | const float TYPE_INDICATOR_BORDER_THICKNESS = M_PI / 128.0f; 13 | 14 | static void set_color_for_state(cairo_t *cairo, struct swaylock_state *state, 15 | struct swaylock_colorset *colorset) { 16 | if (state->auth_state == AUTH_STATE_VALIDATING) { 17 | cairo_set_source_u32(cairo, colorset->verifying); 18 | } else if (state->auth_state == AUTH_STATE_INVALID) { 19 | cairo_set_source_u32(cairo, colorset->wrong); 20 | } else if (state->auth_state == AUTH_STATE_CLEAR) { 21 | cairo_set_source_u32(cairo, colorset->cleared); 22 | } else { 23 | if (state->xkb.caps_lock && state->args.show_caps_lock_indicator) { 24 | cairo_set_source_u32(cairo, colorset->caps_lock); 25 | } else if (state->xkb.caps_lock && !state->args.show_caps_lock_indicator && 26 | state->args.show_caps_lock_text) { 27 | uint32_t inputtextcolor = state->args.colors.text.input; 28 | state->args.colors.text.input = state->args.colors.text.caps_lock; 29 | cairo_set_source_u32(cairo, colorset->input); 30 | state->args.colors.text.input = inputtextcolor; 31 | } else { 32 | cairo_set_source_u32(cairo, colorset->input); 33 | } 34 | } 35 | } 36 | 37 | static void timetext(struct swaylock_surface *surface, char **tstr, char **dstr) { 38 | static char dbuf[256]; 39 | static char tbuf[256]; 40 | 41 | // Use user's locale for strftime calls 42 | char *prevloc = setlocale(LC_TIME, NULL); 43 | setlocale(LC_TIME, ""); 44 | 45 | time_t t = time(NULL); 46 | struct tm *tm = localtime(&t); 47 | 48 | if (surface->state->args.timestr[0]) { 49 | strftime(tbuf, sizeof(tbuf), surface->state->args.timestr, tm); 50 | *tstr = tbuf; 51 | } else { 52 | *tstr = NULL; 53 | } 54 | 55 | if (surface->state->args.datestr[0]) { 56 | strftime(dbuf, sizeof(dbuf), surface->state->args.datestr, tm); 57 | *dstr = dbuf; 58 | } else { 59 | *dstr = NULL; 60 | } 61 | 62 | // Set it back, so we don't break stuff 63 | setlocale(LC_TIME, prevloc); 64 | } 65 | 66 | void render_frame_background(struct swaylock_surface *surface) { 67 | struct swaylock_state *state = surface->state; 68 | 69 | int buffer_width = surface->width * surface->scale; 70 | int buffer_height = surface->height * surface->scale; 71 | if (buffer_width == 0 || buffer_height == 0) { 72 | return; // not yet configured 73 | } 74 | 75 | surface->current_buffer = get_next_buffer(state->shm, 76 | surface->buffers, buffer_width, buffer_height); 77 | if (surface->current_buffer == NULL) { 78 | return; 79 | } 80 | 81 | cairo_t *cairo = surface->current_buffer->cairo; 82 | cairo_set_antialias(cairo, CAIRO_ANTIALIAS_BEST); 83 | 84 | cairo_save(cairo); 85 | cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE); 86 | cairo_set_source_u32(cairo, state->args.colors.background); 87 | cairo_paint(cairo); 88 | if (surface->image && state->args.mode != BACKGROUND_MODE_SOLID_COLOR) { 89 | cairo_set_operator(cairo, CAIRO_OPERATOR_OVER); 90 | render_background_image(cairo, surface->image, 91 | state->args.mode, buffer_width, buffer_height); 92 | } 93 | cairo_restore(cairo); 94 | cairo_identity_matrix(cairo); 95 | 96 | wl_surface_set_buffer_scale(surface->surface, surface->scale); 97 | wl_surface_attach(surface->surface, surface->current_buffer->buffer, 0, 0); 98 | wl_surface_damage_buffer(surface->surface, 0, 0, INT32_MAX, INT32_MAX); 99 | wl_surface_commit(surface->surface); 100 | } 101 | 102 | void render_background_fade(struct swaylock_surface *surface, uint32_t time) { 103 | struct swaylock_state *state = surface->state; 104 | 105 | int buffer_width = surface->width * surface->scale; 106 | int buffer_height = surface->height * surface->scale; 107 | if (buffer_width == 0 || buffer_height == 0) { 108 | return; // not yet configured 109 | } 110 | 111 | if (fade_is_complete(&surface->fade)) { 112 | return; 113 | } 114 | 115 | surface->current_buffer = get_next_buffer(state->shm, 116 | surface->buffers, buffer_width, buffer_height); 117 | if (surface->current_buffer == NULL) { 118 | return; 119 | } 120 | 121 | fade_update(&surface->fade, surface->current_buffer, time); 122 | 123 | wl_surface_set_buffer_scale(surface->surface, surface->scale); 124 | wl_surface_attach(surface->surface, surface->current_buffer->buffer, 0, 0); 125 | wl_surface_damage(surface->surface, 0, 0, surface->width, surface->height); 126 | wl_surface_commit(surface->surface); 127 | } 128 | 129 | void render_background_fade_prepare(struct swaylock_surface *surface, struct pool_buffer *buffer) { 130 | if (fade_is_complete(&surface->fade)) { 131 | return; 132 | } 133 | 134 | fade_prepare(&surface->fade, buffer); 135 | 136 | wl_surface_set_buffer_scale(surface->surface, surface->scale); 137 | wl_surface_attach(surface->surface, surface->current_buffer->buffer, 0, 0); 138 | wl_surface_damage(surface->surface, 0, 0, surface->width, surface->height); 139 | wl_surface_commit(surface->surface); 140 | } 141 | 142 | void render_frame(struct swaylock_surface *surface) { 143 | struct swaylock_state *state = surface->state; 144 | 145 | int arc_radius = state->args.radius * surface->scale; 146 | int arc_thickness = state->args.thickness * surface->scale; 147 | int buffer_diameter = (arc_radius + arc_thickness) * 2; 148 | 149 | int buffer_width = surface->indicator_width; 150 | int buffer_height = surface->indicator_height; 151 | int new_width = buffer_diameter; 152 | int new_height = buffer_diameter; 153 | 154 | int subsurf_xpos; 155 | int subsurf_ypos; 156 | 157 | // Center the indicator unless overridden by the user 158 | if (state->args.override_indicator_x_position) { 159 | subsurf_xpos = state->args.indicator_x_position - 160 | buffer_width / (2 * surface->scale) + 2 / surface->scale; 161 | } else { 162 | subsurf_xpos = surface->width / 2 - 163 | buffer_width / (2 * surface->scale) + 2 / surface->scale; 164 | } 165 | 166 | if (state->args.override_indicator_y_position) { 167 | subsurf_ypos = state->args.indicator_y_position - 168 | (state->args.radius + state->args.thickness); 169 | } else { 170 | subsurf_ypos = surface->height / 2 - 171 | (state->args.radius + state->args.thickness); 172 | } 173 | 174 | wl_subsurface_set_position(surface->subsurface, subsurf_xpos, subsurf_ypos); 175 | 176 | surface->current_buffer = get_next_buffer(state->shm, 177 | surface->indicator_buffers, buffer_width, buffer_height); 178 | if (surface->current_buffer == NULL) { 179 | return; 180 | } 181 | 182 | // Hide subsurface until we want it visible 183 | wl_surface_attach(surface->child, NULL, 0, 0); 184 | wl_surface_commit(surface->child); 185 | 186 | cairo_t *cairo = surface->current_buffer->cairo; 187 | cairo_set_antialias(cairo, CAIRO_ANTIALIAS_BEST); 188 | cairo_font_options_t *fo = cairo_font_options_create(); 189 | cairo_font_options_set_hint_style(fo, CAIRO_HINT_STYLE_FULL); 190 | cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_SUBPIXEL); 191 | cairo_font_options_set_subpixel_order(fo, to_cairo_subpixel_order(surface->subpixel)); 192 | cairo_set_font_options(cairo, fo); 193 | cairo_font_options_destroy(fo); 194 | cairo_identity_matrix(cairo); 195 | 196 | // Clear 197 | cairo_save(cairo); 198 | cairo_set_source_rgba(cairo, 0, 0, 0, 0); 199 | cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE); 200 | cairo_paint(cairo); 201 | cairo_restore(cairo); 202 | 203 | float type_indicator_border_thickness = 204 | TYPE_INDICATOR_BORDER_THICKNESS * surface->scale; 205 | 206 | // This is a bit messy. 207 | // After the fork, upstream added their own --indicator-idle-visible option, 208 | // but it works slightly differently from swaylock-effects' --indicator 209 | // option. To maintain compatibility with upstream swaylock scripts as well 210 | // as with old swaylock-effects scripts, I will keep both flags. 211 | bool upstream_show_indicator = 212 | state->args.show_indicator && (state->auth_state != AUTH_STATE_IDLE || 213 | state->args.indicator_idle_visible); 214 | 215 | if (state->args.indicator || 216 | (upstream_show_indicator && state->auth_state != AUTH_STATE_GRACE)) { 217 | // Fill inner circle 218 | cairo_set_line_width(cairo, 0); 219 | cairo_arc(cairo, buffer_width / 2, buffer_diameter / 2, 220 | arc_radius - arc_thickness / 2, 0, 2 * M_PI); 221 | set_color_for_state(cairo, state, &state->args.colors.inside); 222 | cairo_fill_preserve(cairo); 223 | cairo_stroke(cairo); 224 | 225 | // Draw ring 226 | cairo_set_line_width(cairo, arc_thickness); 227 | cairo_arc(cairo, buffer_width / 2, buffer_diameter / 2, arc_radius, 228 | 0, 2 * M_PI); 229 | set_color_for_state(cairo, state, &state->args.colors.ring); 230 | cairo_stroke(cairo); 231 | 232 | // Draw a message 233 | char *text = NULL; 234 | char *text_l1 = NULL; 235 | char *text_l2 = NULL; 236 | const char *layout_text = NULL; 237 | double font_size; 238 | char attempts[4]; // like i3lock: count no more than 999 239 | set_color_for_state(cairo, state, &state->args.colors.text); 240 | cairo_select_font_face(cairo, state->args.font, 241 | CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL); 242 | if (state->args.font_size > 0) { 243 | font_size = state->args.font_size; 244 | } else { 245 | font_size = arc_radius / 3.0f; 246 | } 247 | cairo_set_font_size(cairo, font_size); 248 | switch (state->auth_state) { 249 | case AUTH_STATE_VALIDATING: 250 | text = "verifying"; 251 | break; 252 | case AUTH_STATE_INVALID: 253 | text = "wrong"; 254 | break; 255 | case AUTH_STATE_CLEAR: 256 | text = "cleared"; 257 | break; 258 | case AUTH_STATE_INPUT: 259 | case AUTH_STATE_INPUT_NOP: 260 | case AUTH_STATE_BACKSPACE: 261 | // Caps Lock has higher priority 262 | if (state->xkb.caps_lock && state->args.show_caps_lock_text) { 263 | text = "Caps Lock"; 264 | } else if (state->args.show_failed_attempts && 265 | state->failed_attempts > 0) { 266 | if (state->failed_attempts > 999) { 267 | text = "999+"; 268 | } else { 269 | snprintf(attempts, sizeof(attempts), "%d", state->failed_attempts); 270 | text = attempts; 271 | } 272 | } else if (state->args.clock) { 273 | timetext(surface, &text_l1, &text_l2); 274 | } 275 | 276 | xkb_layout_index_t num_layout = xkb_keymap_num_layouts(state->xkb.keymap); 277 | if (!state->args.hide_keyboard_layout && 278 | (state->args.show_keyboard_layout || num_layout > 1)) { 279 | xkb_layout_index_t curr_layout = 0; 280 | 281 | // advance to the first active layout (if any) 282 | while (curr_layout < num_layout && 283 | xkb_state_layout_index_is_active(state->xkb.state, 284 | curr_layout, XKB_STATE_LAYOUT_EFFECTIVE) != 1) { 285 | ++curr_layout; 286 | } 287 | // will handle invalid index if none are active 288 | layout_text = xkb_keymap_layout_get_name(state->xkb.keymap, curr_layout); 289 | } 290 | break; 291 | default: 292 | if (state->args.clock) 293 | timetext(surface, &text_l1, &text_l2); 294 | break; 295 | } 296 | 297 | if (text_l1 && !text_l2) 298 | text = text_l1; 299 | if (text_l2 && !text_l1) 300 | text = text_l2; 301 | 302 | if (text) { 303 | cairo_text_extents_t extents; 304 | cairo_font_extents_t fe; 305 | double x, y; 306 | cairo_text_extents(cairo, text, &extents); 307 | cairo_font_extents(cairo, &fe); 308 | x = (buffer_width / 2) - 309 | (extents.width / 2 + extents.x_bearing); 310 | y = (buffer_diameter / 2) + 311 | (fe.height / 2 - fe.descent); 312 | 313 | cairo_move_to(cairo, x, y); 314 | cairo_show_text(cairo, text); 315 | cairo_close_path(cairo); 316 | cairo_new_sub_path(cairo); 317 | 318 | if (new_width < extents.width) { 319 | new_width = extents.width; 320 | } 321 | } else if (text_l1 && text_l2) { 322 | cairo_text_extents_t extents_l1, extents_l2; 323 | cairo_font_extents_t fe_l1, fe_l2; 324 | double x_l1, y_l1, x_l2, y_l2; 325 | 326 | /* Top */ 327 | 328 | cairo_text_extents(cairo, text_l1, &extents_l1); 329 | cairo_font_extents(cairo, &fe_l1); 330 | x_l1 = (buffer_width / 2) - 331 | (extents_l1.width / 2 + extents_l1.x_bearing); 332 | y_l1 = (buffer_diameter / 2) + 333 | (fe_l1.height / 2 - fe_l1.descent) - arc_radius / 10.0f; 334 | 335 | cairo_move_to(cairo, x_l1, y_l1); 336 | cairo_show_text(cairo, text_l1); 337 | cairo_close_path(cairo); 338 | cairo_new_sub_path(cairo); 339 | 340 | /* Bottom */ 341 | 342 | cairo_set_font_size(cairo, arc_radius / 6.0f); 343 | cairo_text_extents(cairo, text_l2, &extents_l2); 344 | cairo_font_extents(cairo, &fe_l2); 345 | x_l2 = (buffer_width / 2) - 346 | (extents_l2.width / 2 + extents_l2.x_bearing); 347 | y_l2 = (buffer_diameter / 2) + 348 | (fe_l2.height / 2 - fe_l2.descent) + arc_radius / 3.5f; 349 | 350 | cairo_move_to(cairo, x_l2, y_l2); 351 | cairo_show_text(cairo, text_l2); 352 | cairo_close_path(cairo); 353 | cairo_new_sub_path(cairo); 354 | 355 | if (new_width < extents_l1.width) 356 | new_width = extents_l1.width; 357 | if (new_width < extents_l2.width) 358 | new_width = extents_l2.width; 359 | 360 | 361 | cairo_set_font_size(cairo, font_size); 362 | } 363 | 364 | // Typing indicator: Highlight random part on keypress 365 | if (state->auth_state == AUTH_STATE_INPUT 366 | || state->auth_state == AUTH_STATE_BACKSPACE) { 367 | 368 | static double highlight_start = 0; 369 | if (state->indicator_dirty) { 370 | highlight_start += 371 | (rand() % (int)(M_PI * 100)) / 100.0 + M_PI * 0.5; 372 | state->indicator_dirty = false; 373 | } 374 | 375 | cairo_arc(cairo, buffer_width / 2, buffer_diameter / 2, 376 | arc_radius, highlight_start, 377 | highlight_start + TYPE_INDICATOR_RANGE); 378 | if (state->auth_state == AUTH_STATE_INPUT) { 379 | if (state->xkb.caps_lock && state->args.show_caps_lock_indicator) { 380 | cairo_set_source_u32(cairo, state->args.colors.caps_lock_key_highlight); 381 | } else { 382 | cairo_set_source_u32(cairo, state->args.colors.key_highlight); 383 | } 384 | } else { 385 | if (state->xkb.caps_lock && state->args.show_caps_lock_indicator) { 386 | cairo_set_source_u32(cairo, state->args.colors.caps_lock_bs_highlight); 387 | } else { 388 | cairo_set_source_u32(cairo, state->args.colors.bs_highlight); 389 | } 390 | } 391 | cairo_stroke(cairo); 392 | 393 | // Draw borders 394 | cairo_set_source_u32(cairo, state->args.colors.separator); 395 | cairo_arc(cairo, buffer_width / 2, buffer_diameter / 2, 396 | arc_radius, highlight_start, 397 | highlight_start + type_indicator_border_thickness); 398 | cairo_stroke(cairo); 399 | 400 | cairo_arc(cairo, buffer_width / 2, buffer_diameter / 2, 401 | arc_radius, highlight_start + TYPE_INDICATOR_RANGE, 402 | highlight_start + TYPE_INDICATOR_RANGE + 403 | type_indicator_border_thickness); 404 | cairo_stroke(cairo); 405 | } 406 | 407 | // Draw inner + outer border of the circle 408 | set_color_for_state(cairo, state, &state->args.colors.line); 409 | cairo_set_line_width(cairo, 2.0 * surface->scale); 410 | cairo_arc(cairo, buffer_width / 2, buffer_diameter / 2, 411 | arc_radius - arc_thickness / 2, 0, 2 * M_PI); 412 | cairo_stroke(cairo); 413 | cairo_arc(cairo, buffer_width / 2, buffer_diameter / 2, 414 | arc_radius + arc_thickness / 2, 0, 2 * M_PI); 415 | cairo_stroke(cairo); 416 | 417 | // display layout text separately 418 | if (layout_text) { 419 | cairo_text_extents_t extents; 420 | cairo_font_extents_t fe; 421 | double x, y; 422 | double box_padding = 4.0 * surface->scale; 423 | cairo_text_extents(cairo, layout_text, &extents); 424 | cairo_font_extents(cairo, &fe); 425 | // upper left coordinates for box 426 | x = (buffer_width / 2) - (extents.width / 2) - box_padding; 427 | y = buffer_diameter; 428 | 429 | // background box 430 | cairo_rectangle(cairo, x, y, 431 | extents.width + 2.0 * box_padding, 432 | fe.height + 2.0 * box_padding); 433 | cairo_set_source_u32(cairo, state->args.colors.layout_background); 434 | cairo_fill_preserve(cairo); 435 | // border 436 | cairo_set_source_u32(cairo, state->args.colors.layout_border); 437 | cairo_stroke(cairo); 438 | 439 | // take font extents and padding into account 440 | cairo_move_to(cairo, 441 | x - extents.x_bearing + box_padding, 442 | y + (fe.height - fe.descent) + box_padding); 443 | cairo_set_source_u32(cairo, state->args.colors.layout_text); 444 | cairo_show_text(cairo, layout_text); 445 | cairo_new_sub_path(cairo); 446 | 447 | new_height += fe.height + 2 * box_padding; 448 | if (new_width < extents.width + 2 * box_padding) { 449 | new_width = extents.width + 2 * box_padding; 450 | } 451 | } 452 | } 453 | 454 | // Ensure buffer size is multiple of buffer scale - required by protocol 455 | new_height += surface->scale - (new_height % surface->scale); 456 | new_width += surface->scale - (new_width % surface->scale); 457 | 458 | if (buffer_width != new_width || buffer_height != new_height) { 459 | destroy_buffer(surface->current_buffer); 460 | surface->indicator_width = new_width; 461 | surface->indicator_height = new_height; 462 | render_frame(surface); 463 | return; 464 | } 465 | 466 | wl_surface_set_buffer_scale(surface->child, surface->scale); 467 | wl_surface_attach(surface->child, surface->current_buffer->buffer, 0, 0); 468 | wl_surface_damage_buffer(surface->child, 0, 0, INT32_MAX, INT32_MAX); 469 | wl_surface_commit(surface->child); 470 | 471 | wl_surface_commit(surface->surface); 472 | } 473 | 474 | void render_frames(struct swaylock_state *state) { 475 | struct swaylock_surface *surface; 476 | wl_list_for_each(surface, &state->surfaces, link) { 477 | render_frame(surface); 478 | } 479 | } 480 | -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mortie/swaylock-effects/a863c2fd3c8b313f4d39ff03bcb8dad299727480/screenshot.png -------------------------------------------------------------------------------- /seat.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "log.h" 7 | #include "swaylock.h" 8 | #include "seat.h" 9 | #include "loop.h" 10 | 11 | static void keyboard_keymap(void *data, struct wl_keyboard *wl_keyboard, 12 | uint32_t format, int32_t fd, uint32_t size) { 13 | struct swaylock_seat *seat = data; 14 | struct swaylock_state *state = seat->state; 15 | if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) { 16 | close(fd); 17 | swaylock_log(LOG_ERROR, "Unknown keymap format %d, aborting", format); 18 | exit(1); 19 | } 20 | char *map_shm = mmap(NULL, size - 1, PROT_READ, MAP_PRIVATE, fd, 0); 21 | if (map_shm == MAP_FAILED) { 22 | close(fd); 23 | swaylock_log(LOG_ERROR, "Unable to initialize keymap shm, aborting"); 24 | exit(1); 25 | } 26 | struct xkb_keymap *keymap = xkb_keymap_new_from_buffer( 27 | state->xkb.context, map_shm, size - 1, XKB_KEYMAP_FORMAT_TEXT_V1, 28 | XKB_KEYMAP_COMPILE_NO_FLAGS); 29 | munmap(map_shm, size - 1); 30 | close(fd); 31 | assert(keymap); 32 | struct xkb_state *xkb_state = xkb_state_new(keymap); 33 | assert(xkb_state); 34 | xkb_keymap_unref(state->xkb.keymap); 35 | xkb_state_unref(state->xkb.state); 36 | state->xkb.keymap = keymap; 37 | state->xkb.state = xkb_state; 38 | } 39 | 40 | static void keyboard_enter(void *data, struct wl_keyboard *wl_keyboard, 41 | uint32_t serial, struct wl_surface *surface, struct wl_array *keys) { 42 | // Who cares 43 | } 44 | 45 | static void keyboard_leave(void *data, struct wl_keyboard *wl_keyboard, 46 | uint32_t serial, struct wl_surface *surface) { 47 | // Who cares 48 | } 49 | 50 | static void keyboard_repeat(void *data) { 51 | struct swaylock_seat *seat = data; 52 | struct swaylock_state *state = seat->state; 53 | seat->repeat_timer = loop_add_timer( 54 | state->eventloop, seat->repeat_period_ms, keyboard_repeat, seat); 55 | swaylock_handle_key(state, seat->repeat_sym, seat->repeat_codepoint); 56 | } 57 | 58 | static void keyboard_key(void *data, struct wl_keyboard *wl_keyboard, 59 | uint32_t serial, uint32_t time, uint32_t key, uint32_t _key_state) { 60 | struct swaylock_seat *seat = data; 61 | struct swaylock_state *state = seat->state; 62 | enum wl_keyboard_key_state key_state = _key_state; 63 | xkb_keysym_t sym = xkb_state_key_get_one_sym(state->xkb.state, key + 8); 64 | uint32_t keycode = key_state == WL_KEYBOARD_KEY_STATE_PRESSED ? 65 | key + 8 : 0; 66 | uint32_t codepoint = xkb_state_key_get_utf32(state->xkb.state, keycode); 67 | if (key_state == WL_KEYBOARD_KEY_STATE_PRESSED) { 68 | swaylock_handle_key(state, sym, codepoint); 69 | } 70 | 71 | if (seat->repeat_timer) { 72 | loop_remove_timer(seat->state->eventloop, seat->repeat_timer); 73 | seat->repeat_timer = NULL; 74 | } 75 | 76 | if (key_state == WL_KEYBOARD_KEY_STATE_PRESSED && seat->repeat_period_ms > 0) { 77 | seat->repeat_sym = sym; 78 | seat->repeat_codepoint = codepoint; 79 | seat->repeat_timer = loop_add_timer( 80 | seat->state->eventloop, seat->repeat_delay_ms, keyboard_repeat, seat); 81 | } 82 | } 83 | 84 | static void keyboard_modifiers(void *data, struct wl_keyboard *wl_keyboard, 85 | uint32_t serial, uint32_t mods_depressed, uint32_t mods_latched, 86 | uint32_t mods_locked, uint32_t group) { 87 | struct swaylock_seat *seat = data; 88 | struct swaylock_state *state = seat->state; 89 | int layout_same = xkb_state_layout_index_is_active(state->xkb.state, 90 | group, XKB_STATE_LAYOUT_EFFECTIVE); 91 | if (!layout_same) { 92 | damage_state(state); 93 | } 94 | xkb_state_update_mask(state->xkb.state, 95 | mods_depressed, mods_latched, mods_locked, 0, 0, group); 96 | int caps_lock = xkb_state_mod_name_is_active(state->xkb.state, 97 | XKB_MOD_NAME_CAPS, XKB_STATE_MODS_LOCKED); 98 | if (caps_lock != state->xkb.caps_lock) { 99 | state->xkb.caps_lock = caps_lock; 100 | damage_state(state); 101 | } 102 | state->xkb.control = xkb_state_mod_name_is_active(state->xkb.state, 103 | XKB_MOD_NAME_CTRL, 104 | XKB_STATE_MODS_DEPRESSED | XKB_STATE_MODS_LATCHED); 105 | } 106 | 107 | static void keyboard_repeat_info(void *data, struct wl_keyboard *wl_keyboard, 108 | int32_t rate, int32_t delay) { 109 | struct swaylock_seat *seat = data; 110 | if (rate <= 0) { 111 | seat->repeat_period_ms = -1; 112 | } else { 113 | // Keys per second -> milliseconds between keys 114 | seat->repeat_period_ms = 1000 / rate; 115 | } 116 | seat->repeat_delay_ms = delay; 117 | } 118 | 119 | static const struct wl_keyboard_listener keyboard_listener = { 120 | .keymap = keyboard_keymap, 121 | .enter = keyboard_enter, 122 | .leave = keyboard_leave, 123 | .key = keyboard_key, 124 | .modifiers = keyboard_modifiers, 125 | .repeat_info = keyboard_repeat_info, 126 | }; 127 | 128 | static void wl_pointer_enter(void *data, struct wl_pointer *wl_pointer, 129 | uint32_t serial, struct wl_surface *surface, 130 | wl_fixed_t surface_x, wl_fixed_t surface_y) { 131 | wl_pointer_set_cursor(wl_pointer, serial, NULL, 0, 0); 132 | } 133 | 134 | static void wl_pointer_leave(void *data, struct wl_pointer *wl_pointer, 135 | uint32_t serial, struct wl_surface *surface) { 136 | // Who cares 137 | } 138 | 139 | static void wl_pointer_motion(void *data, struct wl_pointer *wl_pointer, 140 | uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y) { 141 | swaylock_handle_mouse((struct swaylock_state *)data); 142 | } 143 | 144 | static void wl_pointer_button(void *data, struct wl_pointer *wl_pointer, 145 | uint32_t serial, uint32_t time, uint32_t button, uint32_t state) { 146 | swaylock_handle_mouse((struct swaylock_state *)data); 147 | } 148 | 149 | static void wl_pointer_axis(void *data, struct wl_pointer *wl_pointer, 150 | uint32_t time, uint32_t axis, wl_fixed_t value) { 151 | swaylock_handle_mouse((struct swaylock_state *)data); 152 | } 153 | 154 | static void wl_pointer_frame(void *data, struct wl_pointer *wl_pointer) { 155 | // Who cares 156 | } 157 | 158 | static void wl_pointer_axis_source(void *data, struct wl_pointer *wl_pointer, 159 | uint32_t axis_source) { 160 | // Who cares 161 | } 162 | 163 | static void wl_pointer_axis_stop(void *data, struct wl_pointer *wl_pointer, 164 | uint32_t time, uint32_t axis) { 165 | // Who cares 166 | } 167 | 168 | static void wl_pointer_axis_discrete(void *data, struct wl_pointer *wl_pointer, 169 | uint32_t axis, int32_t discrete) { 170 | // Who cares 171 | } 172 | 173 | static const struct wl_pointer_listener pointer_listener = { 174 | .enter = wl_pointer_enter, 175 | .leave = wl_pointer_leave, 176 | .motion = wl_pointer_motion, 177 | .button = wl_pointer_button, 178 | .axis = wl_pointer_axis, 179 | .frame = wl_pointer_frame, 180 | .axis_source = wl_pointer_axis_source, 181 | .axis_stop = wl_pointer_axis_stop, 182 | .axis_discrete = wl_pointer_axis_discrete, 183 | }; 184 | 185 | static void wl_touch_down(void *data, struct wl_touch *touch, uint32_t serial, 186 | uint32_t time, struct wl_surface *surface, int32_t id, wl_fixed_t x, wl_fixed_t y) { 187 | swaylock_handle_touch((struct swaylock_state *)data); 188 | } 189 | 190 | static void wl_touch_up(void *data, struct wl_touch *touch, uint32_t serial, 191 | uint32_t time, int32_t id) { 192 | // Who cares 193 | } 194 | 195 | static void wl_touch_motion(void *data, struct wl_touch *touch, uint32_t time, 196 | int32_t id, wl_fixed_t x, wl_fixed_t y) { 197 | swaylock_handle_touch((struct swaylock_state *)data); 198 | } 199 | 200 | static void wl_touch_frame(void *data, struct wl_touch *touch) { 201 | // Who cares 202 | } 203 | 204 | static void wl_touch_cancel(void *data, struct wl_touch *touch) { 205 | // Who cares 206 | } 207 | 208 | static const struct wl_touch_listener touch_listener = { 209 | .down = wl_touch_down, 210 | .up = wl_touch_up, 211 | .motion = wl_touch_motion, 212 | .frame = wl_touch_frame, 213 | .cancel = wl_touch_cancel, 214 | }; 215 | 216 | static void seat_handle_capabilities(void *data, struct wl_seat *wl_seat, 217 | enum wl_seat_capability caps) { 218 | struct swaylock_seat *seat = data; 219 | if (seat->pointer) { 220 | wl_pointer_release(seat->pointer); 221 | seat->pointer = NULL; 222 | } 223 | if (seat->keyboard) { 224 | wl_keyboard_release(seat->keyboard); 225 | seat->keyboard = NULL; 226 | } 227 | if ((caps & WL_SEAT_CAPABILITY_POINTER)) { 228 | seat->pointer = wl_seat_get_pointer(wl_seat); 229 | wl_pointer_add_listener(seat->pointer, &pointer_listener, seat->state); 230 | } 231 | if ((caps & WL_SEAT_CAPABILITY_KEYBOARD)) { 232 | seat->keyboard = wl_seat_get_keyboard(wl_seat); 233 | wl_keyboard_add_listener(seat->keyboard, &keyboard_listener, seat); 234 | } 235 | if ((caps & WL_SEAT_CAPABILITY_TOUCH)) { 236 | seat->touch = wl_seat_get_touch(wl_seat); 237 | wl_touch_add_listener(seat->touch, &touch_listener, seat->state); 238 | } 239 | } 240 | 241 | static void seat_handle_name(void *data, struct wl_seat *wl_seat, 242 | const char *name) { 243 | // Who cares 244 | } 245 | 246 | const struct wl_seat_listener seat_listener = { 247 | .capabilities = seat_handle_capabilities, 248 | .name = seat_handle_name, 249 | }; 250 | -------------------------------------------------------------------------------- /shadow.c: -------------------------------------------------------------------------------- 1 | #define _XOPEN_SOURCE // for crypt 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #ifdef __GLIBC__ 9 | // GNU, you damn slimy bastard 10 | #include 11 | #endif 12 | #include "comm.h" 13 | #include "log.h" 14 | #include "swaylock.h" 15 | 16 | void initialize_pw_backend(int argc, char **argv) { 17 | if (geteuid() != 0) { 18 | swaylock_log(LOG_ERROR, 19 | "swaylock needs to be setuid to read /etc/shadow"); 20 | exit(EXIT_FAILURE); 21 | } 22 | 23 | if (!spawn_comm_child()) { 24 | exit(EXIT_FAILURE); 25 | } 26 | 27 | if (setgid(getgid()) != 0) { 28 | swaylock_log_errno(LOG_ERROR, "Unable to drop root"); 29 | exit(EXIT_FAILURE); 30 | } 31 | if (setuid(getuid()) != 0) { 32 | swaylock_log_errno(LOG_ERROR, "Unable to drop root"); 33 | exit(EXIT_FAILURE); 34 | } 35 | if (setuid(0) != -1) { 36 | swaylock_log_errno(LOG_ERROR, "Unable to drop root (we shouldn't be " 37 | "able to restore it after setuid)"); 38 | exit(EXIT_FAILURE); 39 | } 40 | } 41 | 42 | void run_pw_backend_child(void) { 43 | /* This code runs as root */ 44 | struct passwd *pwent = getpwuid(getuid()); 45 | if (!pwent) { 46 | swaylock_log_errno(LOG_ERROR, "failed to getpwuid"); 47 | exit(EXIT_FAILURE); 48 | } 49 | char *encpw = pwent->pw_passwd; 50 | if (strcmp(encpw, "x") == 0) { 51 | struct spwd *swent = getspnam(pwent->pw_name); 52 | if (!swent) { 53 | swaylock_log_errno(LOG_ERROR, "failed to getspnam"); 54 | exit(EXIT_FAILURE); 55 | } 56 | encpw = swent->sp_pwdp; 57 | } 58 | 59 | /* We don't need any additional logging here because the parent process will 60 | * also fail here and will handle logging for us. */ 61 | if (setgid(getgid()) != 0) { 62 | exit(EXIT_FAILURE); 63 | } 64 | if (setuid(getuid()) != 0) { 65 | exit(EXIT_FAILURE); 66 | } 67 | if (setuid(0) != -1) { 68 | exit(EXIT_FAILURE); 69 | } 70 | 71 | /* This code does not run as root */ 72 | swaylock_log(LOG_DEBUG, "Prepared to authorize user %s", pwent->pw_name); 73 | 74 | while (1) { 75 | char *buf; 76 | ssize_t size = read_comm_request(&buf); 77 | if (size < 0) { 78 | exit(EXIT_FAILURE); 79 | } else if (size == 0) { 80 | break; 81 | } 82 | 83 | char *c = crypt(buf, encpw); 84 | if (c == NULL) { 85 | swaylock_log_errno(LOG_ERROR, "crypt failed"); 86 | clear_buffer(buf, size); 87 | exit(EXIT_FAILURE); 88 | } 89 | bool success = strcmp(c, encpw) == 0; 90 | 91 | if (!write_comm_reply(success)) { 92 | clear_buffer(buf, size); 93 | exit(EXIT_FAILURE); 94 | } 95 | 96 | // We don't want to keep it in memory longer than necessary, 97 | // so clear *before* sleeping. 98 | clear_buffer(buf, size); 99 | free(buf); 100 | 101 | sleep(2); 102 | } 103 | 104 | clear_buffer(encpw, strlen(encpw)); 105 | exit(EXIT_SUCCESS); 106 | } -------------------------------------------------------------------------------- /swaylock.1.scd: -------------------------------------------------------------------------------- 1 | swaylock(1) 2 | 3 | # NAME 4 | 5 | swaylock-effects - Fancier screen locker for Wayland 6 | 7 | # SYNOPSIS 8 | 9 | _swaylock_ [options...] 10 | 11 | Locks your Wayland session. 12 | 13 | # OPTIONS 14 | 15 | *-C, --config* 16 | The config file to use. By default, the following paths are checked: 17 | _$HOME/.swaylock/config_, _$XDG\_CONFIG\_HOME/swaylock/config_, and 18 | _SYSCONFDIR/swaylock/config_. All flags aside from this one are valid 19 | options in the configuration file using the format _long-option=value_. 20 | For options such as _ignore-empty-password_, just supply the _long-option_. 21 | All leading dashes should be omitted and the equals sign is required for 22 | flags that take an argument. 23 | 24 | *-d, --debug* 25 | Enable debugging output. 26 | 27 | *-e, --ignore-empty-password* 28 | When an empty password is provided, do not validate it. 29 | 30 | *-F, --show-failed-attempts* 31 | Show current count of failed authentication attempts. 32 | 33 | *-f, --daemonize* 34 | Detach from the controlling terminal after locking. 35 | 36 | Note: this is the default behavior of i3lock. 37 | 38 | *--fade-in* 39 | Fade in the lock screen. 40 | 41 | *--submit-on-touch* 42 | Submit password in response to a touch event. 43 | 44 | *--grace* 45 | Only require a password after some grace period. 46 | 47 | Note: Together with *--indicator*, the indicator is always shown. 48 | Together with *--indicator-idle-visible*, the indicator is only shown 49 | after the grace period. 50 | 51 | *--grace-no-mouse* 52 | With *--grace*, don't unlock in response to a mouse event. 53 | 54 | *--grace-no-touch* 55 | With *--grace*, don't unlock in response to a touch event. 56 | 57 | *-h, --help* 58 | Show help message and quit. 59 | 60 | *-v, --version* 61 | Show the version number and quit. 62 | 63 | # APPEARANCE 64 | 65 | *-u, --no-unlock-indicator* 66 | Disable the unlock indicator. 67 | 68 | *--indicator* 69 | Always show the indicator. 70 | 71 | *--clock* 72 | Show a clock in the indicator. 73 | 74 | *--timestr* 75 | The time format for the indicator clock. Defaults to '%T'. 76 | 77 | *--datestr* 78 | The date format for the indicator clock. Defaults to '%a, %x'. 79 | 80 | *-i, --image* [[]:] 81 | Display the given image, optionally only on the given output. Use -c to set 82 | a background color. If the path potentially contains a ':', prefix it with another 83 | ':' to prevent interpreting part of it as . 84 | 85 | *-S, --screenshots* 86 | Display a screenshot. 87 | 88 | *-k, --show-keyboard-layout* 89 | Display the current xkb layout while typing. 90 | 91 | *-K, --hide-keyboard-layout* 92 | Force hiding the current xkb layout while typing, even if more than one layout 93 | is configured or the show-keyboard-layout option is set. 94 | 95 | *-L, --disable-caps-lock-text* 96 | Disable the Caps Lock text. 97 | 98 | *-l, --indicator-caps-lock* 99 | Show the current Caps Lock state also on the indicator. 100 | 101 | *-s, --scaling* 102 | Image scaling mode: _stretch_, _fill_, _fit_, _center_, _tile_, 103 | _solid\_color_. Use _solid\_color_ to display only the background color, even 104 | if a background image is specified. 105 | 106 | *-t, --tiling* 107 | Same as --scaling=tile. 108 | 109 | *-c, --color* 110 | Turn the screen into the given color instead of white. If -i is used, this 111 | sets the background of the image to the given color. Defaults to white 112 | (FFFFFF). 113 | 114 | *--bs-hl-color* 115 | Sets the color of backspace highlight segments. 116 | 117 | *--caps-lock-bs-hl-color* 118 | Sets the color of backspace highlight segments when Caps Lock is active. 119 | 120 | *--caps-lock-key-hl-color* 121 | Sets the color of the key press highlight segments when Caps Lock is active. 122 | 123 | *--font* 124 | Sets the font of the text. 125 | 126 | *--font-size* 127 | Sets a fixed font size for the indicator text. 128 | 129 | *--indicator-idle-visible* 130 | Sets the indicator to show even if idle. 131 | 132 | *--indicator-radius* 133 | Sets the indicator radius. The default value is 50. 134 | 135 | *--indicator-thickness* 136 | Sets the indicator thickness. The default value is 10. 137 | 138 | *--indicator-x-position* 139 | Sets the horizontal position of the indicator. 140 | 141 | *--indicator-y-position* 142 | Sets the vertical position of the indicator. 143 | 144 | *--inside-color* 145 | Sets the color of the inside of the indicator. 146 | 147 | *--inside-clear-color* 148 | Sets the color of the inside of the indicator when cleared. 149 | 150 | *--inside-caps-lock-color* 151 | Sets the color of the inside of the indicator when Caps Lock is active. 152 | 153 | *--inside-ver-color* 154 | Sets the color of the inside of the indicator when verifying. 155 | 156 | *--inside-wrong-color* 157 | Sets the color of the inside of the indicator when invalid. 158 | 159 | *--key-hl-color* 160 | Sets the color of the key press highlight segments. 161 | 162 | *--layout-bg-color* 163 | Sets the background color of the box containing the layout text. 164 | 165 | *--layout-border-color* 166 | Sets the color of the border of the box containing the layout text. 167 | 168 | *--layout-text-color* 169 | Sets the color of the layout text. 170 | 171 | *--line-color* 172 | Sets the color of the line between the inside and ring. 173 | 174 | *--line-clear-color* 175 | Sets the color of the line between the inside and ring when cleared. 176 | 177 | *--line-caps-lock-color* 178 | Sets the color of the line between the inside and ring when Caps Lock is 179 | active. 180 | 181 | *--line-ver-color* 182 | Sets the color of the line between the inside and ring when verifying. 183 | 184 | *--line-wrong-color* 185 | Sets the color of the line between the inside and ring when invalid. 186 | 187 | *-n, --line-uses-inside* 188 | Use the inside color for the line between the inside and ring. 189 | 190 | *-r, --line-uses-ring* 191 | Use the ring color for the line between the inside and ring. 192 | 193 | *--ring-color* 194 | Sets the color of the ring of the indicator when typing or idle. 195 | 196 | *--ring-clear-color* 197 | Sets the color of the ring of the indicator when cleared. 198 | 199 | *--ring-caps-lock-color* 200 | Sets the color of the ring of the indicator when Caps Lock is active. 201 | 202 | *--ring-ver-color* 203 | Sets the color of the ring of the indicator when verifying. 204 | 205 | *--ring-wrong-color* 206 | Sets the color of the ring of the indicator when invalid. 207 | 208 | *--separator-color* 209 | Sets the color of the lines that separate highlight segments. 210 | 211 | *--text-color* 212 | Sets the color of the text. 213 | 214 | *--text-clear-color* 215 | Sets the color of the text when cleared. 216 | 217 | *--text-caps-lock-color* 218 | Sets the color of the text when Caps Lock is active. 219 | 220 | *--text-ver-color* 221 | Sets the color of the text when verifying. 222 | 223 | *--text-wrong-color* 224 | Sets the color of the text when invalid. 225 | 226 | *--effect-blur* x 227 | Blur displayed images. 228 | 229 | *--effect-pixelate* 230 | Pixelate displayed images. 231 | 232 | *--effect-scale* 233 | Scale the image by a factor. This can be used to 234 | make other effects faster if you don't need the full resolution. 235 | 236 | *--effect-greyscale* 237 | Make the displayed image greyscale. 238 | 239 | *--effect-vignette :* 240 | Apply a vignette effect to images. 241 | Base and factor should be numbers between 0 and 1. 242 | 243 | *--effect-compose ;;;* 244 | Overlay another image to your lock screen. 245 | The _position_, _size_ and _gravity_ part is optional. 246 | 247 | *--effect-custom * 248 | Load a custom effect from a shared object. The .so must export a++ 249 | *void swaylock_effect(uint32\_t \*data, int width, int height, int scale)*++ 250 | or an *uint32\_t swaylock_pixel(uint32\_t pix, int x, int y, int width, int height)*. 251 | 252 | *--time-effects* 253 | Measure the time it takes to run each effect. 254 | 255 | # AUTHORS 256 | 257 | Maintained by Martin Dørum, forked from upstream Swaylock which is maintained 258 | by Drew DeVault. 259 | For more information about swaylock-effects development, see 260 | https://github.com/mortie/swaylock-effects. 261 | -------------------------------------------------------------------------------- /unicode.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "unicode.h" 4 | 5 | size_t utf8_chsize(uint32_t ch) { 6 | if (ch < 0x80) { 7 | return 1; 8 | } else if (ch < 0x800) { 9 | return 2; 10 | } else if (ch < 0x10000) { 11 | return 3; 12 | } 13 | return 4; 14 | } 15 | 16 | size_t utf8_encode(char *str, uint32_t ch) { 17 | size_t len = 0; 18 | uint8_t first; 19 | 20 | if (ch < 0x80) { 21 | first = 0; 22 | len = 1; 23 | } else if (ch < 0x800) { 24 | first = 0xc0; 25 | len = 2; 26 | } else if (ch < 0x10000) { 27 | first = 0xe0; 28 | len = 3; 29 | } else { 30 | first = 0xf0; 31 | len = 4; 32 | } 33 | 34 | for (size_t i = len - 1; i > 0; --i) { 35 | str[i] = (ch & 0x3f) | 0x80; 36 | ch >>= 6; 37 | } 38 | 39 | str[0] = ch | first; 40 | return len; 41 | } 42 | 43 | 44 | static const struct { 45 | uint8_t mask; 46 | uint8_t result; 47 | int octets; 48 | } sizes[] = { 49 | { 0x80, 0x00, 1 }, 50 | { 0xE0, 0xC0, 2 }, 51 | { 0xF0, 0xE0, 3 }, 52 | { 0xF8, 0xF0, 4 }, 53 | { 0xFC, 0xF8, 5 }, 54 | { 0xFE, 0xF8, 6 }, 55 | { 0x80, 0x80, -1 }, 56 | }; 57 | 58 | int utf8_size(const char *s) { 59 | uint8_t c = (uint8_t)*s; 60 | for (size_t i = 0; i < sizeof(sizes) / sizeof(*sizes); ++i) { 61 | if ((c & sizes[i].mask) == sizes[i].result) { 62 | return sizes[i].octets; 63 | } 64 | } 65 | return -1; 66 | } 67 | -------------------------------------------------------------------------------- /wlr-input-inhibitor-unstable-v1.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Copyright © 2018 Drew DeVault 5 | 6 | Permission to use, copy, modify, distribute, and sell this 7 | software and its documentation for any purpose is hereby granted 8 | without fee, provided that the above copyright notice appear in 9 | all copies and that both that copyright notice and this permission 10 | notice appear in supporting documentation, and that the name of 11 | the copyright holders not be used in advertising or publicity 12 | pertaining to distribution of the software without specific, 13 | written prior permission. The copyright holders make no 14 | representations about the suitability of this software for any 15 | purpose. It is provided "as is" without express or implied 16 | warranty. 17 | 18 | THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS 19 | SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND 20 | FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY 21 | SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 22 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN 23 | AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, 24 | ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF 25 | THIS SOFTWARE. 26 | 27 | 28 | 29 | 30 | Clients can use this interface to prevent input events from being sent to 31 | any surfaces but its own, which is useful for example in lock screen 32 | software. It is assumed that access to this interface will be locked down 33 | to whitelisted clients by the compositor. 34 | 35 | 36 | 37 | 38 | Activates the input inhibitor. As long as the inhibitor is active, the 39 | compositor will not send input events to other clients. 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | While this resource exists, input to clients other than the owner of the 52 | inhibitor resource will not receive input events. The client that owns 53 | this resource will receive all input events normally. The compositor will 54 | also disable all of its own input processing (such as keyboard shortcuts) 55 | while the inhibitor is active. 56 | 57 | The compositor may continue to send input events to selected clients, 58 | such as an on-screen keyboard (via the input-method protocol). 59 | 60 | 61 | 62 | 63 | Destroy the inhibitor and allow other clients to receive input. 64 | 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /wlr-layer-shell-unstable-v1.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Copyright © 2017 Drew DeVault 5 | 6 | Permission to use, copy, modify, distribute, and sell this 7 | software and its documentation for any purpose is hereby granted 8 | without fee, provided that the above copyright notice appear in 9 | all copies and that both that copyright notice and this permission 10 | notice appear in supporting documentation, and that the name of 11 | the copyright holders not be used in advertising or publicity 12 | pertaining to distribution of the software without specific, 13 | written prior permission. The copyright holders make no 14 | representations about the suitability of this software for any 15 | purpose. It is provided "as is" without express or implied 16 | warranty. 17 | 18 | THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS 19 | SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND 20 | FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY 21 | SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 22 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN 23 | AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, 24 | ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF 25 | THIS SOFTWARE. 26 | 27 | 28 | 29 | 30 | Clients can use this interface to assign the surface_layer role to 31 | wl_surfaces. Such surfaces are assigned to a "layer" of the output and 32 | rendered with a defined z-depth respective to each other. They may also be 33 | anchored to the edges and corners of a screen and specify input handling 34 | semantics. This interface should be suitable for the implementation of 35 | many desktop shell components, and a broad number of other applications 36 | that interact with the desktop. 37 | 38 | 39 | 40 | 41 | Create a layer surface for an existing surface. This assigns the role of 42 | layer_surface, or raises a protocol error if another role is already 43 | assigned. 44 | 45 | Creating a layer surface from a wl_surface which has a buffer attached 46 | or committed is a client error, and any attempts by a client to attach 47 | or manipulate a buffer prior to the first layer_surface.configure call 48 | must also be treated as errors. 49 | 50 | You may pass NULL for output to allow the compositor to decide which 51 | output to use. Generally this will be the one that the user most 52 | recently interacted with. 53 | 54 | Clients can specify a namespace that defines the purpose of the layer 55 | surface. 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | These values indicate which layers a surface can be rendered in. They 73 | are ordered by z depth, bottom-most first. Traditional shell surfaces 74 | will typically be rendered between the bottom and top layers. 75 | Fullscreen shell surfaces are typically rendered at the top layer. 76 | Multiple surfaces can share a single layer, and ordering within a 77 | single layer is undefined. 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | An interface that may be implemented by a wl_surface, for surfaces that 90 | are designed to be rendered as a layer of a stacked desktop-like 91 | environment. 92 | 93 | Layer surface state (size, anchor, exclusive zone, margin, interactivity) 94 | is double-buffered, and will be applied at the time wl_surface.commit of 95 | the corresponding wl_surface is called. 96 | 97 | 98 | 99 | 100 | Sets the size of the surface in surface-local coordinates. The 101 | compositor will display the surface centered with respect to its 102 | anchors. 103 | 104 | If you pass 0 for either value, the compositor will assign it and 105 | inform you of the assignment in the configure event. You must set your 106 | anchor to opposite edges in the dimensions you omit; not doing so is a 107 | protocol error. Both values are 0 by default. 108 | 109 | Size is double-buffered, see wl_surface.commit. 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | Requests that the compositor anchor the surface to the specified edges 118 | and corners. If two orthoginal edges are specified (e.g. 'top' and 119 | 'left'), then the anchor point will be the intersection of the edges 120 | (e.g. the top left corner of the output); otherwise the anchor point 121 | will be centered on that edge, or in the center if none is specified. 122 | 123 | Anchor is double-buffered, see wl_surface.commit. 124 | 125 | 126 | 127 | 128 | 129 | 130 | Requests that the compositor avoids occluding an area of the surface 131 | with other surfaces. The compositor's use of this information is 132 | implementation-dependent - do not assume that this region will not 133 | actually be occluded. 134 | 135 | A positive value is only meaningful if the surface is anchored to an 136 | edge, rather than a corner. The zone is the number of surface-local 137 | coordinates from the edge that are considered exclusive. 138 | 139 | Surfaces that do not wish to have an exclusive zone may instead specify 140 | how they should interact with surfaces that do. If set to zero, the 141 | surface indicates that it would like to be moved to avoid occluding 142 | surfaces with a positive excluzive zone. If set to -1, the surface 143 | indicates that it would not like to be moved to accommodate for other 144 | surfaces, and the compositor should extend it all the way to the edges 145 | it is anchored to. 146 | 147 | For example, a panel might set its exclusive zone to 10, so that 148 | maximized shell surfaces are not shown on top of it. A notification 149 | might set its exclusive zone to 0, so that it is moved to avoid 150 | occluding the panel, but shell surfaces are shown underneath it. A 151 | wallpaper or lock screen might set their exclusive zone to -1, so that 152 | they stretch below or over the panel. 153 | 154 | The default value is 0. 155 | 156 | Exclusive zone is double-buffered, see wl_surface.commit. 157 | 158 | 159 | 160 | 161 | 162 | 163 | Requests that the surface be placed some distance away from the anchor 164 | point on the output, in surface-local coordinates. Setting this value 165 | for edges you are not anchored to has no effect. 166 | 167 | The exclusive zone includes the margin. 168 | 169 | Margin is double-buffered, see wl_surface.commit. 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | Set to 1 to request that the seat send keyboard events to this layer 180 | surface. For layers below the shell surface layer, the seat will use 181 | normal focus semantics. For layers above the shell surface layers, the 182 | seat will always give exclusive keyboard focus to the top-most layer 183 | which has keyboard interactivity set to true. 184 | 185 | Layer surfaces receive pointer, touch, and tablet events normally. If 186 | you do not want to receive them, set the input region on your surface 187 | to an empty region. 188 | 189 | Events is double-buffered, see wl_surface.commit. 190 | 191 | 192 | 193 | 194 | 195 | 196 | This assigns an xdg_popup's parent to this layer_surface. This popup 197 | should have been created via xdg_surface::get_popup with the parent set 198 | to NULL, and this request must be invoked before committing the popup's 199 | initial state. 200 | 201 | See the documentation of xdg_popup for more details about what an 202 | xdg_popup is and how it is used. 203 | 204 | 205 | 206 | 207 | 208 | 209 | When a configure event is received, if a client commits the 210 | surface in response to the configure event, then the client 211 | must make an ack_configure request sometime before the commit 212 | request, passing along the serial of the configure event. 213 | 214 | If the client receives multiple configure events before it 215 | can respond to one, it only has to ack the last configure event. 216 | 217 | A client is not required to commit immediately after sending 218 | an ack_configure request - it may even ack_configure several times 219 | before its next surface commit. 220 | 221 | A client may send multiple ack_configure requests before committing, but 222 | only the last request sent before a commit indicates which configure 223 | event the client really is responding to. 224 | 225 | 226 | 227 | 228 | 229 | 230 | This request destroys the layer surface. 231 | 232 | 233 | 234 | 235 | 236 | The configure event asks the client to resize its surface. 237 | 238 | Clients should arrange their surface for the new states, and then send 239 | an ack_configure request with the serial sent in this configure event at 240 | some point before committing the new surface. 241 | 242 | The client is free to dismiss all but the last configure event it 243 | received. 244 | 245 | The width and height arguments specify the size of the window in 246 | surface-local coordinates. 247 | 248 | The size is a hint, in the sense that the client is free to ignore it if 249 | it doesn't resize, pick a smaller size (to satisfy aspect ratio or 250 | resize in steps of NxM pixels). If the client picks a smaller size and 251 | is anchored to two opposite anchors (e.g. 'top' and 'bottom'), the 252 | surface will be centered on this axis. 253 | 254 | If the width or height arguments are zero, it means the client should 255 | decide its own window dimension. 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | The closed event is sent by the compositor when the surface will no 265 | longer be shown. The output may have been destroyed or the user may 266 | have asked for it to be removed. Further changes to the surface will be 267 | ignored. The client should destroy the resource after receiving this 268 | event, and create a new surface if they so choose. 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | -------------------------------------------------------------------------------- /wlr-screencopy-unstable-v1.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Copyright © 2018 Simon Ser 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a 7 | copy of this software and associated documentation files (the "Software"), 8 | to deal in the Software without restriction, including without limitation 9 | the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 | and/or sell copies of the Software, and to permit persons to whom the 11 | Software is furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice (including the next 14 | paragraph) shall be included in all copies or substantial portions of the 15 | Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 20 | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 22 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | 25 | 26 | 27 | This protocol allows clients to ask the compositor to copy part of the 28 | screen content to a client buffer. 29 | 30 | Warning! The protocol described in this file is experimental and 31 | backward incompatible changes may be made. Backward compatible changes 32 | may be added together with the corresponding interface version bump. 33 | Backward incompatible changes are done by bumping the version number in 34 | the protocol and interface names and resetting the interface version. 35 | Once the protocol is to be declared stable, the 'z' prefix and the 36 | version number in the protocol and interface names are removed and the 37 | interface version number is reset. 38 | 39 | 40 | 41 | 42 | This object is a manager which offers requests to start capturing from a 43 | source. 44 | 45 | 46 | 47 | 48 | Capture the next frame of an entire output. 49 | 50 | 51 | 53 | 54 | 55 | 56 | 57 | 58 | Capture the next frame of an output's region. 59 | 60 | The region is given in output logical coordinates, see 61 | xdg_output.logical_size. The region will be clipped to the output's 62 | extents. 63 | 64 | 65 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | All objects created by the manager will still remain valid, until their 77 | appropriate destroy request has been called. 78 | 79 | 80 | 81 | 82 | 83 | 84 | This object represents a single frame. 85 | 86 | When created, a "buffer" event will be sent. The client will then be able 87 | to send a "copy" request. If the capture is successful, the compositor 88 | will send a "flags" followed by a "ready" event. 89 | 90 | If the capture failed, the "failed" event is sent. This can happen anytime 91 | before the "ready" event. 92 | 93 | Once either a "ready" or a "failed" event is received, the client should 94 | destroy the frame. 95 | 96 | 97 | 98 | 99 | Provides information about the frame's buffer. This event is sent once 100 | as soon as the frame is created. 101 | 102 | The client should then create a buffer with the provided attributes, and 103 | send a "copy" request. 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | Copy the frame to the supplied buffer. The buffer must have a the 114 | correct size, see zwlr_screencopy_frame_v1.buffer. The buffer needs to 115 | have a supported format. 116 | 117 | If the frame is successfully copied, a "flags" and a "ready" events are 118 | sent. Otherwise, a "failed" event is sent. 119 | 120 | 121 | 122 | 123 | 124 | 126 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | Provides flags about the frame. This event is sent once before the 137 | "ready" event. 138 | 139 | 140 | 141 | 142 | 143 | 144 | Called as soon as the frame is copied, indicating it is available 145 | for reading. This event includes the time at which presentation happened 146 | at. 147 | 148 | The timestamp is expressed as tv_sec_hi, tv_sec_lo, tv_nsec triples, 149 | each component being an unsigned 32-bit value. Whole seconds are in 150 | tv_sec which is a 64-bit value combined from tv_sec_hi and tv_sec_lo, 151 | and the additional fractional part in tv_nsec as nanoseconds. Hence, 152 | for valid timestamps tv_nsec must be in [0, 999999999]. The seconds part 153 | may have an arbitrary offset at start. 154 | 155 | After receiving this event, the client should destroy the object. 156 | 157 | 159 | 161 | 163 | 164 | 165 | 166 | 167 | This event indicates that the attempted frame copy has failed. 168 | 169 | After receiving this event, the client should destroy the object. 170 | 171 | 172 | 173 | 174 | 175 | Destroys the frame. This request can be sent at any time by the client. 176 | 177 | 178 | 179 | 180 | --------------------------------------------------------------------------------