Convert swaylock into standalone project

This commit is contained in:
Drew DeVault 2019-01-13 21:15:32 -05:00
parent 43435cd731
commit b90637e2a6
26 changed files with 1838 additions and 19 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
build

121
background-image.c Normal file
View File

@ -0,0 +1,121 @@
#include <assert.h>
#include <stdbool.h>
#include <wlr/util/log.h>
#include "background-image.h"
#include "cairo.h"
enum background_mode parse_background_mode(const char *mode) {
if (strcmp(mode, "stretch") == 0) {
return BACKGROUND_MODE_STRETCH;
} else if (strcmp(mode, "fill") == 0) {
return BACKGROUND_MODE_FILL;
} else if (strcmp(mode, "fit") == 0) {
return BACKGROUND_MODE_FIT;
} else if (strcmp(mode, "center") == 0) {
return BACKGROUND_MODE_CENTER;
} else if (strcmp(mode, "tile") == 0) {
return BACKGROUND_MODE_TILE;
} else if (strcmp(mode, "solid_color") == 0) {
return BACKGROUND_MODE_SOLID_COLOR;
}
wlr_log(WLR_ERROR, "Unsupported background mode: %s", mode);
return BACKGROUND_MODE_INVALID;
}
cairo_surface_t *load_background_image(const char *path) {
cairo_surface_t *image;
#if HAVE_GDK_PIXBUF
GError *err = NULL;
GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(path, &err);
if (!pixbuf) {
wlr_log(WLR_ERROR, "Failed to load background image (%s).",
err->message);
return false;
}
image = gdk_cairo_image_surface_create_from_pixbuf(pixbuf);
g_object_unref(pixbuf);
#else
image = cairo_image_surface_create_from_png(path);
#endif // HAVE_GDK_PIXBUF
if (!image) {
wlr_log(WLR_ERROR, "Failed to read background image.");
return NULL;
}
if (cairo_surface_status(image) != CAIRO_STATUS_SUCCESS) {
wlr_log(WLR_ERROR, "Failed to read background image: %s."
#if !HAVE_GDK_PIXBUF
"\nSway was compiled without gdk_pixbuf support, so only"
"\nPNG images can be loaded. This is the likely cause."
#endif // !HAVE_GDK_PIXBUF
, cairo_status_to_string(cairo_surface_status(image)));
return NULL;
}
return image;
}
void render_background_image(cairo_t *cairo, cairo_surface_t *image,
enum background_mode mode, int buffer_width, int buffer_height) {
double width = cairo_image_surface_get_width(image);
double height = cairo_image_surface_get_height(image);
cairo_save(cairo);
switch (mode) {
case BACKGROUND_MODE_STRETCH:
cairo_scale(cairo,
(double)buffer_width / width,
(double)buffer_height / height);
cairo_set_source_surface(cairo, image, 0, 0);
break;
case BACKGROUND_MODE_FILL: {
double window_ratio = (double)buffer_width / buffer_height;
double bg_ratio = width / height;
if (window_ratio > bg_ratio) {
double scale = (double)buffer_width / width;
cairo_scale(cairo, scale, scale);
cairo_set_source_surface(cairo, image,
0, (double)buffer_height / 2 / scale - height / 2);
} else {
double scale = (double)buffer_height / height;
cairo_scale(cairo, scale, scale);
cairo_set_source_surface(cairo, image,
(double)buffer_width / 2 / scale - width / 2, 0);
}
break;
}
case BACKGROUND_MODE_FIT: {
double window_ratio = (double)buffer_width / buffer_height;
double bg_ratio = width / height;
if (window_ratio > bg_ratio) {
double scale = (double)buffer_height / height;
cairo_scale(cairo, scale, scale);
cairo_set_source_surface(cairo, image,
(double)buffer_width / 2 / scale - width / 2, 0);
} else {
double scale = (double)buffer_width / width;
cairo_scale(cairo, scale, scale);
cairo_set_source_surface(cairo, image,
0, (double)buffer_height / 2 / scale - height / 2);
}
break;
}
case BACKGROUND_MODE_CENTER:
cairo_set_source_surface(cairo, image,
(double)buffer_width / 2 - width / 2,
(double)buffer_height / 2 - height / 2);
break;
case BACKGROUND_MODE_TILE: {
cairo_pattern_t *pattern = cairo_pattern_create_for_surface(image);
cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT);
cairo_set_source(cairo, pattern);
break;
}
case BACKGROUND_MODE_SOLID_COLOR:
case BACKGROUND_MODE_INVALID:
assert(0);
break;
}
cairo_paint(cairo);
cairo_restore(cairo);
}

143
cairo.c Normal file
View File

@ -0,0 +1,143 @@
#include <stdint.h>
#include <cairo/cairo.h>
#include "cairo.h"
#if HAVE_GDK_PIXBUF
#include <gdk-pixbuf/gdk-pixbuf.h>
#endif
void cairo_set_source_u32(cairo_t *cairo, uint32_t color) {
cairo_set_source_rgba(cairo,
(color >> (3*8) & 0xFF) / 255.0,
(color >> (2*8) & 0xFF) / 255.0,
(color >> (1*8) & 0xFF) / 255.0,
(color >> (0*8) & 0xFF) / 255.0);
}
cairo_subpixel_order_t to_cairo_subpixel_order(enum wl_output_subpixel subpixel) {
switch (subpixel) {
case WL_OUTPUT_SUBPIXEL_HORIZONTAL_RGB:
return CAIRO_SUBPIXEL_ORDER_RGB;
case WL_OUTPUT_SUBPIXEL_HORIZONTAL_BGR:
return CAIRO_SUBPIXEL_ORDER_BGR;
case WL_OUTPUT_SUBPIXEL_VERTICAL_RGB:
return CAIRO_SUBPIXEL_ORDER_VRGB;
case WL_OUTPUT_SUBPIXEL_VERTICAL_BGR:
return CAIRO_SUBPIXEL_ORDER_VBGR;
default:
return CAIRO_SUBPIXEL_ORDER_DEFAULT;
}
return CAIRO_SUBPIXEL_ORDER_DEFAULT;
}
cairo_surface_t *cairo_image_surface_scale(cairo_surface_t *image,
int width, int height) {
int image_width = cairo_image_surface_get_width(image);
int image_height = cairo_image_surface_get_height(image);
cairo_surface_t *new =
cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
cairo_t *cairo = cairo_create(new);
cairo_scale(cairo, (double)width / image_width,
(double)height / image_height);
cairo_set_source_surface(cairo, image, 0, 0);
cairo_paint(cairo);
cairo_destroy(cairo);
return new;
}
#if HAVE_GDK_PIXBUF
cairo_surface_t* gdk_cairo_image_surface_create_from_pixbuf(const GdkPixbuf *gdkbuf) {
int chan = gdk_pixbuf_get_n_channels(gdkbuf);
if (chan < 3) {
return NULL;
}
const guint8* gdkpix = gdk_pixbuf_read_pixels(gdkbuf);
if (!gdkpix) {
return NULL;
}
gint w = gdk_pixbuf_get_width(gdkbuf);
gint h = gdk_pixbuf_get_height(gdkbuf);
int stride = gdk_pixbuf_get_rowstride(gdkbuf);
cairo_format_t fmt = (chan == 3) ? CAIRO_FORMAT_RGB24 : CAIRO_FORMAT_ARGB32;
cairo_surface_t * cs = cairo_image_surface_create (fmt, w, h);
cairo_surface_flush (cs);
if ( !cs || cairo_surface_status(cs) != CAIRO_STATUS_SUCCESS) {
return NULL;
}
int cstride = cairo_image_surface_get_stride(cs);
unsigned char * cpix = cairo_image_surface_get_data(cs);
if (chan == 3) {
int i;
for (i = h; i; --i) {
const guint8 *gp = gdkpix;
unsigned char *cp = cpix;
const guint8* end = gp + 3*w;
while (gp < end) {
#if G_BYTE_ORDER == G_LITTLE_ENDIAN
cp[0] = gp[2];
cp[1] = gp[1];
cp[2] = gp[0];
#else
cp[1] = gp[0];
cp[2] = gp[1];
cp[3] = gp[2];
#endif
gp += 3;
cp += 4;
}
gdkpix += stride;
cpix += cstride;
}
} else {
/* premul-color = alpha/255 * color/255 * 255 = (alpha*color)/255
* (z/255) = z/256 * 256/255 = z/256 (1 + 1/255)
* = z/256 + (z/256)/255 = (z + z/255)/256
* # recurse once
* = (z + (z + z/255)/256)/256
* = (z + z/256 + z/256/255) / 256
* # only use 16bit uint operations, loose some precision,
* # result is floored.
* -> (z + z>>8)>>8
* # add 0x80/255 = 0.5 to convert floor to round
* => (z+0x80 + (z+0x80)>>8 ) >> 8
* ------
* tested as equal to lround(z/255.0) for uint z in [0..0xfe02]
*/
#define PREMUL_ALPHA(x,a,b,z) \
G_STMT_START { z = a * b + 0x80; x = (z + (z >> 8)) >> 8; } \
G_STMT_END
int i;
for (i = h; i; --i) {
const guint8 *gp = gdkpix;
unsigned char *cp = cpix;
const guint8* end = gp + 4*w;
guint z1, z2, z3;
while (gp < end) {
#if G_BYTE_ORDER == G_LITTLE_ENDIAN
PREMUL_ALPHA(cp[0], gp[2], gp[3], z1);
PREMUL_ALPHA(cp[1], gp[1], gp[3], z2);
PREMUL_ALPHA(cp[2], gp[0], gp[3], z3);
cp[3] = gp[3];
#else
PREMUL_ALPHA(cp[1], gp[0], gp[3], z1);
PREMUL_ALPHA(cp[2], gp[1], gp[3], z2);
PREMUL_ALPHA(cp[3], gp[2], gp[3], z3);
cp[0] = gp[3];
#endif
gp += 4;
cp += 4;
}
gdkpix += stride;
cpix += cstride;
}
#undef PREMUL_ALPHA
}
cairo_surface_mark_dirty(cs);
return cs;
}
#endif // HAVE_GDK_PIXBUF

View File

@ -0,0 +1,20 @@
#ifndef _SWAY_BACKGROUND_IMAGE_H
#define _SWAY_BACKGROUND_IMAGE_H
#include "cairo.h"
enum background_mode {
BACKGROUND_MODE_STRETCH,
BACKGROUND_MODE_FILL,
BACKGROUND_MODE_FIT,
BACKGROUND_MODE_CENTER,
BACKGROUND_MODE_TILE,
BACKGROUND_MODE_SOLID_COLOR,
BACKGROUND_MODE_INVALID,
};
enum background_mode parse_background_mode(const char *mode);
cairo_surface_t *load_background_image(const char *path);
void render_background_image(cairo_t *cairo, cairo_surface_t *image,
enum background_mode mode, int buffer_width, int buffer_height);
#endif

25
include/cairo.h Normal file
View File

@ -0,0 +1,25 @@
#ifndef _SWAY_CAIRO_H
#define _SWAY_CAIRO_H
#include "config.h"
#include <stdint.h>
#include <cairo/cairo.h>
#include <wayland-client.h>
#if HAVE_GDK_PIXBUF
#include <gdk-pixbuf/gdk-pixbuf.h>
#endif
void cairo_set_source_u32(cairo_t *cairo, uint32_t color);
cairo_subpixel_order_t to_cairo_subpixel_order(enum wl_output_subpixel subpixel);
cairo_surface_t *cairo_image_surface_scale(cairo_surface_t *image,
int width, int height);
#if HAVE_GDK_PIXBUF
cairo_surface_t* gdk_cairo_image_surface_create_from_pixbuf(
const GdkPixbuf *gdkbuf);
#endif // HAVE_GDK_PIXBUF
#endif

35
include/list.h Normal file
View File

@ -0,0 +1,35 @@
#ifndef _SWAY_LIST_H
#define _SWAY_LIST_H
typedef struct {
int capacity;
int length;
void **items;
} list_t;
list_t *create_list(void);
void list_free(list_t *list);
void list_add(list_t *list, void *item);
void list_insert(list_t *list, int index, void *item);
void list_del(list_t *list, int index);
void list_cat(list_t *list, list_t *source);
// See qsort. Remember to use *_qsort functions as compare functions,
// because they dereference the left and right arguments first!
void list_qsort(list_t *list, int compare(const void *left, const void *right));
// Return index for first item in list that returns 0 for given compare
// function or -1 if none matches.
int list_seq_find(list_t *list, int compare(const void *item, const void *cmp_to), const void *cmp_to);
int list_find(list_t *list, const void *item);
// stable sort since qsort is not guaranteed to be stable
void list_stable_sort(list_t *list, int compare(const void *a, const void *b));
// swap two elements in a list
void list_swap(list_t *list, int src, int dest);
// move item to end of list
void list_move_to_end(list_t *list, void *item);
/* Calls `free` for each item in the list, then frees the list.
* Do not use this to free lists of primitives or items that require more
* complicated deallocation code.
*/
void list_free_items_and_destroy(list_t *list);
#endif

54
include/loop.h Normal file
View File

@ -0,0 +1,54 @@
#ifndef _SWAY_LOOP_H
#define _SWAY_LOOP_H
#include <stdbool.h>
/**
* This is an event loop system designed for sway clients, not sway itself.
*
* The loop consists of file descriptors and timers. Typically the Wayland
* display's file descriptor will be one of the fds in the loop.
*/
struct loop;
struct loop_timer;
/**
* Create an event loop.
*/
struct loop *loop_create(void);
/**
* Destroy the event loop (eg. on program termination).
*/
void loop_destroy(struct loop *loop);
/**
* Poll the event loop. This will block until one of the fds has data.
*/
void loop_poll(struct loop *loop);
/**
* Add a file descriptor to the loop.
*/
void loop_add_fd(struct loop *loop, int fd, short mask,
void (*func)(int fd, short mask, void *data), void *data);
/**
* Add a timer to the loop.
*
* When the timer expires, the timer will be removed from the loop and freed.
*/
struct loop_timer *loop_add_timer(struct loop *loop, int ms,
void (*callback)(void *data), void *data);
/**
* Remove a file descriptor from the loop.
*/
bool loop_remove_fd(struct loop *loop, int fd);
/**
* Remove a timer from the loop.
*/
bool loop_remove_timer(struct loop *loop, struct loop_timer *timer);
#endif

1
include/meson.build Normal file
View File

@ -0,0 +1 @@
configure_file(output: 'config.h', configuration: conf_data)

24
include/pool-buffer.h Normal file
View File

@ -0,0 +1,24 @@
#ifndef _SWAY_BUFFERS_H
#define _SWAY_BUFFERS_H
#include <cairo/cairo.h>
#include <pango/pangocairo.h>
#include <stdbool.h>
#include <stdint.h>
#include <wayland-client.h>
struct pool_buffer {
struct wl_buffer *buffer;
cairo_surface_t *surface;
cairo_t *cairo;
PangoContext *pango;
uint32_t width, height;
void *data;
size_t size;
bool busy;
};
struct pool_buffer *get_next_buffer(struct wl_shm *shm,
struct pool_buffer pool[static 2], uint32_t width, uint32_t height);
void destroy_buffer(struct pool_buffer *buffer);
#endif

21
include/seat.h Normal file
View File

@ -0,0 +1,21 @@
#ifndef _SWAYLOCK_SEAT_H
#define _SWAYLOCK_SEAT_H
#include <xkbcommon/xkbcommon.h>
struct swaylock_xkb {
bool caps_lock;
bool control;
struct xkb_state *state;
struct xkb_context *context;
struct xkb_keymap *keymap;
};
struct swaylock_seat {
struct swaylock_state *state;
struct wl_pointer *pointer;
struct wl_keyboard *keyboard;
};
extern const struct wl_seat_listener seat_listener;
#endif

112
include/swaylock.h Normal file
View File

@ -0,0 +1,112 @@
#ifndef _SWAYLOCK_H
#define _SWAYLOCK_H
#include <stdbool.h>
#include <stdint.h>
#include <wayland-client.h>
#include "background-image.h"
#include "cairo.h"
#include "pool-buffer.h"
#include "seat.h"
#include "wlr-layer-shell-unstable-v1-client-protocol.h"
enum auth_state {
AUTH_STATE_IDLE,
AUTH_STATE_CLEAR,
AUTH_STATE_INPUT,
AUTH_STATE_INPUT_NOP,
AUTH_STATE_BACKSPACE,
AUTH_STATE_VALIDATING,
AUTH_STATE_INVALID,
};
struct swaylock_colorset {
uint32_t input;
uint32_t cleared;
uint32_t verifying;
uint32_t wrong;
};
struct swaylock_colors {
uint32_t background;
uint32_t bs_highlight;
uint32_t key_highlight;
uint32_t separator;
struct swaylock_colorset inside;
struct swaylock_colorset line;
struct swaylock_colorset ring;
struct swaylock_colorset text;
};
struct swaylock_args {
struct swaylock_colors colors;
enum background_mode mode;
char *font;
uint32_t radius;
uint32_t thickness;
bool ignore_empty;
bool show_indicator;
bool daemonize;
};
struct swaylock_password {
size_t len;
char buffer[1024];
};
struct swaylock_state {
struct loop *eventloop;
struct loop_timer *clear_indicator_timer; // clears the indicator
struct loop_timer *clear_password_timer; // clears the password buffer
struct loop_timer *verify_password_timer;
struct wl_display *display;
struct wl_compositor *compositor;
struct zwlr_layer_shell_v1 *layer_shell;
struct zwlr_input_inhibit_manager_v1 *input_inhibit_manager;
struct wl_shm *shm;
struct wl_list surfaces;
struct wl_list images;
struct swaylock_args args;
struct swaylock_password password;
struct swaylock_xkb xkb;
enum auth_state auth_state;
bool run_display;
struct zxdg_output_manager_v1 *zxdg_output_manager;
};
struct swaylock_surface {
cairo_surface_t *image;
struct swaylock_state *state;
struct wl_output *output;
uint32_t output_global_name;
struct zxdg_output_v1 *xdg_output;
struct wl_surface *surface;
struct zwlr_layer_surface_v1 *layer_surface;
struct pool_buffer buffers[2];
struct pool_buffer *current_buffer;
bool frame_pending, dirty;
uint32_t width, height;
int32_t scale;
enum wl_output_subpixel subpixel;
char *output_name;
struct wl_list link;
};
// There is exactly one swaylock_image for each -i argument
struct swaylock_image {
char *path;
char *output_name;
cairo_surface_t *cairo_surface;
struct wl_list link;
};
void swaylock_handle_key(struct swaylock_state *state,
xkb_keysym_t keysym, uint32_t codepoint);
void render_frame(struct swaylock_surface *surface);
void render_frames(struct swaylock_state *state);
void damage_surface(struct swaylock_surface *surface);
void damage_state(struct swaylock_state *state);
void initialize_pw_backend(void);
bool attempt_password(struct swaylock_password *pw);
void clear_password_buffer(struct swaylock_password *pw);
#endif

33
include/unicode.h Normal file
View File

@ -0,0 +1,33 @@
#ifndef _SWAY_UNICODE_H
#define _SWAY_UNICODE_H
#include <stddef.h>
#include <stdint.h>
// Technically UTF-8 supports up to 6 byte codepoints, but Unicode itself
// doesn't really bother with more than 4.
#define UTF8_MAX_SIZE 4
#define UTF8_INVALID 0x80
/**
* Grabs the next UTF-8 character and advances the string pointer
*/
uint32_t utf8_decode(const char **str);
/**
* Encodes a character as UTF-8 and returns the length of that character.
*/
size_t utf8_encode(char *str, uint32_t ch);
/**
* Returns the size of the next UTF-8 character
*/
int utf8_size(const char *str);
/**
* Returns the size of a UTF-8 character
*/
size_t utf8_chsize(uint32_t ch);
#endif

156
list.c Normal file
View File

@ -0,0 +1,156 @@
#include "list.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
list_t *create_list(void) {
list_t *list = malloc(sizeof(list_t));
if (!list) {
return NULL;
}
list->capacity = 10;
list->length = 0;
list->items = malloc(sizeof(void*) * list->capacity);
return list;
}
static void list_resize(list_t *list) {
if (list->length == list->capacity) {
list->capacity *= 2;
list->items = realloc(list->items, sizeof(void*) * list->capacity);
}
}
void list_free(list_t *list) {
if (list == NULL) {
return;
}
free(list->items);
free(list);
}
void list_add(list_t *list, void *item) {
list_resize(list);
list->items[list->length++] = item;
}
void list_insert(list_t *list, int index, void *item) {
list_resize(list);
memmove(&list->items[index + 1], &list->items[index], sizeof(void*) * (list->length - index));
list->length++;
list->items[index] = item;
}
void list_del(list_t *list, int index) {
list->length--;
memmove(&list->items[index], &list->items[index + 1], sizeof(void*) * (list->length - index));
}
void list_cat(list_t *list, list_t *source) {
for (int i = 0; i < source->length; ++i) {
list_add(list, source->items[i]);
}
}
void list_qsort(list_t *list, int compare(const void *left, const void *right)) {
qsort(list->items, list->length, sizeof(void *), compare);
}
int list_seq_find(list_t *list, int compare(const void *item, const void *data), const void *data) {
for (int i = 0; i < list->length; i++) {
void *item = list->items[i];
if (compare(item, data) == 0) {
return i;
}
}
return -1;
}
int list_find(list_t *list, const void *item) {
for (int i = 0; i < list->length; i++) {
if (list->items[i] == item) {
return i;
}
}
return -1;
}
void list_swap(list_t *list, int src, int dest) {
void *tmp = list->items[src];
list->items[src] = list->items[dest];
list->items[dest] = tmp;
}
void list_move_to_end(list_t *list, void *item) {
int i;
for (i = 0; i < list->length; ++i) {
if (list->items[i] == item) {
break;
}
}
list_del(list, i);
list_add(list, item);
}
static void list_rotate(list_t *list, int from, int to) {
void *tmp = list->items[to];
while (to > from) {
list->items[to] = list->items[to - 1];
to--;
}
list->items[from] = tmp;
}
static void list_inplace_merge(list_t *list, int left, int last, int mid, int compare(const void *a, const void *b)) {
int right = mid + 1;
if (compare(&list->items[mid], &list->items[right]) <= 0) {
return;
}
while (left <= mid && right <= last) {
if (compare(&list->items[left], &list->items[right]) <= 0) {
left++;
} else {
list_rotate(list, left, right);
left++;
mid++;
right++;
}
}
}
static void list_inplace_sort(list_t *list, int first, int last, int compare(const void *a, const void *b)) {
if (first >= last) {
return;
} else if ((last - first) == 1) {
if (compare(&list->items[first], &list->items[last]) > 0) {
list_swap(list, first, last);
}
} else {
int mid = (int)((last + first) / 2);
list_inplace_sort(list, first, mid, compare);
list_inplace_sort(list, mid + 1, last, compare);
list_inplace_merge(list, first, last, mid, compare);
}
}
void list_stable_sort(list_t *list, int compare(const void *a, const void *b)) {
if (list->length > 1) {
list_inplace_sort(list, 0, list->length - 1, compare);
}
}
void list_free_items_and_destroy(list_t *list) {
if (!list) {
return;
}
for (int i = 0; i < list->length; ++i) {
free(list->items[i]);
}
list_free(list);
}

178
loop.c Normal file
View File

@ -0,0 +1,178 @@
#define _POSIX_C_SOURCE 200112L
#include <limits.h>
#include <string.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include <poll.h>
#include <time.h>
#include <unistd.h>
#include <wlr/util/log.h>
#include "list.h"
#include "loop.h"
struct loop_fd_event {
void (*callback)(int fd, short mask, void *data);
void *data;
};
struct loop_timer {
void (*callback)(void *data);
void *data;
struct timespec expiry;
};
struct loop {
struct pollfd *fds;
int fd_length;
int fd_capacity;
list_t *fd_events; // struct loop_fd_event
list_t *timers; // struct loop_timer
};
struct loop *loop_create(void) {
struct loop *loop = calloc(1, sizeof(struct loop));
if (!loop) {
wlr_log(WLR_ERROR, "Unable to allocate memory for loop");
return NULL;
}
loop->fd_capacity = 10;
loop->fds = malloc(sizeof(struct pollfd) * loop->fd_capacity);
loop->fd_events = create_list();
loop->timers = create_list();
return loop;
}
void loop_destroy(struct loop *loop) {
list_free_items_and_destroy(loop->fd_events);
list_free_items_and_destroy(loop->timers);
free(loop->fds);
free(loop);
}
void loop_poll(struct loop *loop) {
// Calculate next timer in ms
int ms = INT_MAX;
if (loop->timers->length) {
struct timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
for (int i = 0; i < loop->timers->length; ++i) {
struct loop_timer *timer = loop->timers->items[i];
int timer_ms = (timer->expiry.tv_sec - now.tv_sec) * 1000;
timer_ms += (timer->expiry.tv_nsec - now.tv_nsec) / 1000000;
if (timer_ms < ms) {
ms = timer_ms;
}
}
}
if (ms < 0) {
ms = 0;
}
poll(loop->fds, loop->fd_length, ms);
// Dispatch fds
for (int i = 0; i < loop->fd_length; ++i) {
struct pollfd pfd = loop->fds[i];
struct loop_fd_event *event = loop->fd_events->items[i];
// Always send these events
unsigned events = pfd.events | POLLHUP | POLLERR;
if (pfd.revents & events) {
event->callback(pfd.fd, pfd.revents, event->data);
}
}
// Dispatch timers
if (loop->timers->length) {
struct timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
for (int i = 0; i < loop->timers->length; ++i) {
struct loop_timer *timer = loop->timers->items[i];
bool expired = timer->expiry.tv_sec < now.tv_sec ||
(timer->expiry.tv_sec == now.tv_sec &&
timer->expiry.tv_nsec < now.tv_nsec);
if (expired) {
timer->callback(timer->data);
loop_remove_timer(loop, timer);
--i;
}
}
}
}
void loop_add_fd(struct loop *loop, int fd, short mask,
void (*callback)(int fd, short mask, void *data), void *data) {
struct loop_fd_event *event = calloc(1, sizeof(struct loop_fd_event));
if (!event) {
wlr_log(WLR_ERROR, "Unable to allocate memory for event");
return;
}
event->callback = callback;
event->data = data;
list_add(loop->fd_events, event);
struct pollfd pfd = {fd, mask, 0};
if (loop->fd_length == loop->fd_capacity) {
loop->fd_capacity += 10;
loop->fds = realloc(loop->fds,
sizeof(struct pollfd) * loop->fd_capacity);
}
loop->fds[loop->fd_length++] = pfd;
}
struct loop_timer *loop_add_timer(struct loop *loop, int ms,
void (*callback)(void *data), void *data) {
struct loop_timer *timer = calloc(1, sizeof(struct loop_timer));
if (!timer) {
wlr_log(WLR_ERROR, "Unable to allocate memory for timer");
return NULL;
}
timer->callback = callback;
timer->data = data;
clock_gettime(CLOCK_MONOTONIC, &timer->expiry);
timer->expiry.tv_sec += ms / 1000;
long int nsec = (ms % 1000) * 1000000;
if (timer->expiry.tv_nsec + nsec >= 1000000000) {
timer->expiry.tv_sec++;
nsec -= 1000000000;
}
timer->expiry.tv_nsec += nsec;
list_add(loop->timers, timer);
return timer;
}
bool loop_remove_fd(struct loop *loop, int fd) {
for (int i = 0; i < loop->fd_length; ++i) {
if (loop->fds[i].fd == fd) {
free(loop->fd_events->items[i]);
list_del(loop->fd_events, i);
loop->fd_length--;
memmove(&loop->fds[i], &loop->fds[i + 1],
sizeof(struct pollfd) * (loop->fd_length - i));
return true;
}
}
return false;
}
bool loop_remove_timer(struct loop *loop, struct loop_timer *timer) {
for (int i = 0; i < loop->timers->length; ++i) {
if (loop->timers->items[i] == timer) {
list_del(loop->timers, i);
free(timer);
return true;
}
}
return false;
}

44
main.c
View File

@ -16,15 +16,12 @@
#include <wayland-client.h>
#include <wordexp.h>
#include <wlr/util/log.h>
#include "swaylock/seat.h"
#include "swaylock/swaylock.h"
#include "background-image.h"
#include "seat.h"
#include "swaylock.h"
#include "pool-buffer.h"
#include "cairo.h"
#include "log.h"
#include "loop.h"
#include "stringop.h"
#include "util.h"
#include "wlr-input-inhibitor-unstable-v1-client-protocol.h"
#include "wlr-layer-shell-unstable-v1-client-protocol.h"
#include "xdg-output-unstable-v1-client-protocol.h"
@ -33,6 +30,43 @@ void sway_terminate(int exit_code) {
exit(exit_code);
}
void sway_abort(const char *format, ...) {
va_list args;
va_start(args, format);
_wlr_vlog(WLR_ERROR, format, args);
va_end(args);
sway_terminate(EXIT_FAILURE);
}
static uint32_t parse_color(const char *color) {
if (color[0] == '#') {
++color;
}
int len = strlen(color);
if (len != 6 && len != 8) {
wlr_log(WLR_DEBUG, "Invalid color %s, defaulting to color 0xFFFFFFFF", color);
return 0xFFFFFFFF;
}
uint32_t res = (uint32_t)strtoul(color, NULL, 16);
if (strlen(color) == 6) {
res = (res << 8) | 0xFF;
}
return res;
}
int lenient_strcmp(char *a, char *b) {
if (a == b) {
return 0;
} else if (!a) {
return -1;
} else if (!b) {
return 1;
} else {
return strcmp(a, b);
}
}
static void daemonize(void) {
int fds[2];
if (pipe(fds) != 0) {

View File

@ -1,4 +1,119 @@
project(
'swaylock',
'c',
license: 'MIT',
default_options: [
'c_std=c11',
'warning_level=2',
'werror=true',
],
)
add_project_arguments(
[
'-Wno-unused-parameter',
'-Wno-unused-result',
'-Wundef',
],
language: 'c',
)
cc = meson.get_compiler('c')
sysconfdir = get_option('sysconfdir')
prefix = get_option('prefix')
is_freebsd = host_machine.system().startswith('freebsd')
add_project_arguments(
'-DSYSCONFDIR="/@0@"'.format(join_paths(prefix, sysconfdir)),
language : 'c')
if is_freebsd
add_project_arguments('-D_C11_SOURCE', language: 'c')
endif
wlroots = dependency('wlroots', fallback: ['wlroots', 'wlroots'])
wayland_client = dependency('wayland-client')
wayland_protos = dependency('wayland-protocols', version: '>=1.14')
xkbcommon = dependency('xkbcommon')
cairo = dependency('cairo')
pango = dependency('pango')
pangocairo = dependency('pangocairo')
gdk_pixbuf = dependency('gdk-pixbuf-2.0', required: false)
libpam = cc.find_library('pam', required: false)
crypt = cc.find_library('crypt', required: false)
math = cc.find_library('m')
git = find_program('git', required: false)
scdoc = find_program('scdoc', required: false)
wayland_scanner = find_program('wayland-scanner')
version = get_option('sway-version')
if version != ''
version = '"@0@"'.format(version)
else
if not git.found()
error('git is required to make the version string')
endif
git_commit_hash = run_command([git.path(), 'describe', '--always', '--tags']).stdout().strip()
git_branch = run_command([git.path(), 'rev-parse', '--abbrev-ref', 'HEAD']).stdout().strip()
version = '"@0@ (" __DATE__ ", branch \'@1@\')"'.format(git_commit_hash, git_branch)
endif
add_project_arguments('-DSWAY_VERSION=@0@'.format(version), language: 'c')
wl_protocol_dir = wayland_protos.get_pkgconfig_variable('pkgdatadir')
if wayland_client.version().version_compare('>=1.14.91')
code_type = 'private-code'
else
code_type = 'code'
endif
wayland_scanner_code = generator(
wayland_scanner,
output: '@BASENAME@-protocol.c',
arguments: [code_type, '@INPUT@', '@OUTPUT@'],
)
wayland_scanner_client = generator(
wayland_scanner,
output: '@BASENAME@-client-protocol.h',
arguments: ['client-header', '@INPUT@', '@OUTPUT@'],
)
client_protos_src = []
client_protos_headers = []
client_protocols = [
[wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'],
[wl_protocol_dir, 'unstable/xdg-output/xdg-output-unstable-v1.xml'],
['wlr-layer-shell-unstable-v1.xml'],
['wlr-input-inhibitor-unstable-v1.xml'],
]
foreach p : client_protocols
xml = join_paths(p)
client_protos_src += wayland_scanner_code.process(xml)
client_protos_headers += wayland_scanner_client.process(xml)
endforeach
lib_client_protos = static_library(
'client_protos',
client_protos_src + client_protos_headers,
dependencies: [wayland_client]
) # for the include directory
client_protos = declare_dependency(
link_with: lib_client_protos,
sources: client_protos_headers,
)
conf_data = configuration_data()
conf_data.set10('HAVE_GDK_PIXBUF', gdk_pixbuf.found())
conf_data.set10('HAVE_TRAY', get_option('enable-tray') and (systemd.found() or elogind.found()))
subdir('include')
dependencies = [
cairo,
@ -13,10 +128,17 @@ dependencies = [
]
sources = [
'main.c',
'password.c',
'render.c',
'seat.c'
'background-image.c',
'cairo.c',
'list.c',
'loop.c',
'main.c',
'pango.c',
'password.c',
'pool-buffer.c',
'render.c',
'seat.c',
'unicode.c',
]
if libpam.found()
@ -31,12 +153,12 @@ else
endif
endif
swaylock_inc = include_directories('include')
executable('swaylock',
sources,
include_directories: [sway_inc],
sources,
include_directories: [swaylock_inc],
dependencies: dependencies,
link_with: [lib_sway_common, lib_sway_client],
install_rpath : rpathdir,
install: true
)

5
meson_options.txt Normal file
View File

@ -0,0 +1,5 @@
option('sway-version', type : 'string', description: 'The version string reported in `sway --version`.')
option('zsh-completions', type: 'boolean', value: true, description: 'Install zsh shell completions.')
option('bash-completions', type: 'boolean', value: true, description: 'Install bash shell completions.')
option('fish-completions', type: 'boolean', value: true, description: 'Install fish shell completions.')
option('enable-tray', type: 'boolean', value: false, description: 'Enable support for swaybar tray')

2
pam.c
View File

@ -6,7 +6,7 @@
#include <string.h>
#include <unistd.h>
#include <wlr/util/log.h>
#include "swaylock/swaylock.h"
#include "swaylock.h"
void initialize_pw_backend(void) {
// TODO: only call pam_start once. keep the same handle the whole time

133
pango.c Normal file
View File

@ -0,0 +1,133 @@
#include <cairo/cairo.h>
#include <pango/pangocairo.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <wlr/util/log.h>
#include "cairo.h"
static const char overflow[] = "[buffer overflow]";
static const int max_chars = 16384;
static char *lenient_strcat(char *dest, const char *src) {
if (dest && src) {
return strcat(dest, src);
}
return dest;
}
size_t escape_markup_text(const char *src, char *dest) {
size_t length = 0;
if (dest) {
dest[0] = '\0';
}
while (src[0]) {
switch (src[0]) {
case '&':
length += 5;
lenient_strcat(dest, "&amp;");
break;
case '<':
length += 4;
lenient_strcat(dest, "&lt;");
break;
case '>':
length += 4;
lenient_strcat(dest, "&gt;");
break;
case '\'':
length += 6;
lenient_strcat(dest, "&apos;");
break;
case '"':
length += 6;
lenient_strcat(dest, "&quot;");
break;
default:
if (dest) {
dest[length] = *src;
dest[length + 1] = '\0';
}
length += 1;
}
src++;
}
return length;
}
PangoLayout *get_pango_layout(cairo_t *cairo, const char *font,
const char *text, double scale, bool markup) {
PangoLayout *layout = pango_cairo_create_layout(cairo);
PangoAttrList *attrs;
if (markup) {
char *buf;
GError *error = NULL;
if (pango_parse_markup(text, -1, 0, &attrs, &buf, NULL, &error)) {
pango_layout_set_text(layout, buf, -1);
free(buf);
} else {
wlr_log(WLR_ERROR, "pango_parse_markup '%s' -> error %s", text,
error->message);
g_error_free(error);
markup = false; // fallback to plain text
}
}
if (!markup) {
attrs = pango_attr_list_new();
pango_layout_set_text(layout, text, -1);
}
pango_attr_list_insert(attrs, pango_attr_scale_new(scale));
PangoFontDescription *desc = pango_font_description_from_string(font);
pango_layout_set_font_description(layout, desc);
pango_layout_set_single_paragraph_mode(layout, 1);
pango_layout_set_attributes(layout, attrs);
pango_attr_list_unref(attrs);
pango_font_description_free(desc);
return layout;
}
void get_text_size(cairo_t *cairo, const char *font, int *width, int *height,
int *baseline, double scale, bool markup, const char *fmt, ...) {
char buf[max_chars];
va_list args;
va_start(args, fmt);
if (vsnprintf(buf, sizeof(buf), fmt, args) >= max_chars) {
strcpy(&buf[sizeof(buf) - sizeof(overflow)], overflow);
}
va_end(args);
PangoLayout *layout = get_pango_layout(cairo, font, buf, scale, markup);
pango_cairo_update_layout(cairo, layout);
pango_layout_get_pixel_size(layout, width, height);
if (baseline) {
*baseline = pango_layout_get_baseline(layout) / PANGO_SCALE;
}
g_object_unref(layout);
}
void pango_printf(cairo_t *cairo, const char *font,
double scale, bool markup, const char *fmt, ...) {
char buf[max_chars];
va_list args;
va_start(args, fmt);
if (vsnprintf(buf, sizeof(buf), fmt, args) >= max_chars) {
strcpy(&buf[sizeof(buf) - sizeof(overflow)], overflow);
}
va_end(args);
PangoLayout *layout = get_pango_layout(cairo, font, buf, scale, markup);
cairo_font_options_t *fo = cairo_font_options_create();
cairo_get_font_options(cairo, fo);
pango_cairo_context_set_font_options(pango_layout_get_context(layout), fo);
cairo_font_options_destroy(fo);
pango_cairo_update_layout(cairo, layout);
pango_cairo_show_layout(cairo, layout);
g_object_unref(layout);
}

View File

@ -6,8 +6,8 @@
#include <unistd.h>
#include <wlr/util/log.h>
#include <xkbcommon/xkbcommon.h>
#include "swaylock/swaylock.h"
#include "swaylock/seat.h"
#include "swaylock.h"
#include "seat.h"
#include "loop.h"
#include "unicode.h"

148
pool-buffer.c Normal file
View File

@ -0,0 +1,148 @@
#define _POSIX_C_SOURCE 200809
#include <assert.h>
#include <cairo/cairo.h>
#include <fcntl.h>
#include <pango/pangocairo.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <unistd.h>
#include <wayland-client.h>
#include "pool-buffer.h"
static bool set_cloexec(int fd) {
long flags = fcntl(fd, F_GETFD);
if (flags == -1) {
return false;
}
if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1) {
return false;
}
return true;
}
static int create_pool_file(size_t size, char **name) {
static const char template[] = "sway-client-XXXXXX";
const char *path = getenv("XDG_RUNTIME_DIR");
if (path == NULL) {
fprintf(stderr, "XDG_RUNTIME_DIR is not set\n");
return -1;
}
size_t name_size = strlen(template) + 1 + strlen(path) + 1;
*name = malloc(name_size);
if (*name == NULL) {
fprintf(stderr, "allocation failed\n");
return -1;
}
snprintf(*name, name_size, "%s/%s", path, template);
int fd = mkstemp(*name);
if (fd < 0) {
return -1;
}
if (!set_cloexec(fd)) {
close(fd);
return -1;
}
if (ftruncate(fd, size) < 0) {
close(fd);
return -1;
}
return fd;
}
static void buffer_release(void *data, struct wl_buffer *wl_buffer) {
struct pool_buffer *buffer = data;
buffer->busy = false;
}
static const struct wl_buffer_listener buffer_listener = {
.release = buffer_release
};
static struct pool_buffer *create_buffer(struct wl_shm *shm,
struct pool_buffer *buf, int32_t width, int32_t height,
uint32_t format) {
uint32_t stride = width * 4;
size_t size = stride * height;
char *name;
int fd = create_pool_file(size, &name);
assert(fd != -1);
void *data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
struct wl_shm_pool *pool = wl_shm_create_pool(shm, fd, size);
buf->buffer = wl_shm_pool_create_buffer(pool, 0,
width, height, stride, format);
wl_shm_pool_destroy(pool);
close(fd);
unlink(name);
free(name);
fd = -1;
buf->size = size;
buf->width = width;
buf->height = height;
buf->data = data;
buf->surface = cairo_image_surface_create_for_data(data,
CAIRO_FORMAT_ARGB32, width, height, stride);
buf->cairo = cairo_create(buf->surface);
buf->pango = pango_cairo_create_context(buf->cairo);
wl_buffer_add_listener(buf->buffer, &buffer_listener, buf);
return buf;
}
void destroy_buffer(struct pool_buffer *buffer) {
if (buffer->buffer) {
wl_buffer_destroy(buffer->buffer);
}
if (buffer->cairo) {
cairo_destroy(buffer->cairo);
}
if (buffer->surface) {
cairo_surface_destroy(buffer->surface);
}
if (buffer->pango) {
g_object_unref(buffer->pango);
}
if (buffer->data) {
munmap(buffer->data, buffer->size);
}
memset(buffer, 0, sizeof(struct pool_buffer));
}
struct pool_buffer *get_next_buffer(struct wl_shm *shm,
struct pool_buffer pool[static 2], uint32_t width, uint32_t height) {
struct pool_buffer *buffer = NULL;
for (size_t i = 0; i < 2; ++i) {
if (pool[i].busy) {
continue;
}
buffer = &pool[i];
}
if (!buffer) {
return NULL;
}
if (buffer->width != width || buffer->height != height) {
destroy_buffer(buffer);
}
if (!buffer->buffer) {
if (!create_buffer(shm, buffer, width, height,
WL_SHM_FORMAT_ARGB8888)) {
return NULL;
}
}
buffer->busy = true;
return buffer;
}

View File

@ -3,7 +3,7 @@
#include <wayland-client.h>
#include "cairo.h"
#include "background-image.h"
#include "swaylock/swaylock.h"
#include "swaylock.h"
#define M_PI 3.14159265358979323846
const float TYPE_INDICATOR_RANGE = M_PI / 3.0f;

4
seat.c
View File

@ -4,8 +4,8 @@
#include <unistd.h>
#include <wlr/util/log.h>
#include <xkbcommon/xkbcommon.h>
#include "swaylock/swaylock.h"
#include "swaylock/seat.h"
#include "swaylock.h"
#include "seat.h"
static void keyboard_keymap(void *data, struct wl_keyboard *wl_keyboard,
uint32_t format, int32_t fd, uint32_t size) {

101
unicode.c Normal file
View File

@ -0,0 +1,101 @@
#include <stdint.h>
#include <stddef.h>
#include "unicode.h"
size_t utf8_chsize(uint32_t ch) {
if (ch < 0x80) {
return 1;
} else if (ch < 0x800) {
return 2;
} else if (ch < 0x10000) {
return 3;
}
return 4;
}
static const uint8_t masks[] = {
0x7F,
0x1F,
0x0F,
0x07,
0x03,
0x01
};
uint32_t utf8_decode(const char **char_str) {
uint8_t **s = (uint8_t **)char_str;
uint32_t cp = 0;
if (**s < 128) {
// shortcut
cp = **s;
++*s;
return cp;
}
int size = utf8_size((char *)*s);
if (size == -1) {
++*s;
return UTF8_INVALID;
}
uint8_t mask = masks[size - 1];
cp = **s & mask;
++*s;
while (--size) {
cp <<= 6;
cp |= **s & 0x3f;
++*s;
}
return cp;
}
size_t utf8_encode(char *str, uint32_t ch) {
size_t len = 0;
uint8_t first;
if (ch < 0x80) {
first = 0;
len = 1;
} else if (ch < 0x800) {
first = 0xc0;
len = 2;
} else if (ch < 0x10000) {
first = 0xe0;
len = 3;
} else {
first = 0xf0;
len = 4;
}
for (size_t i = len - 1; i > 0; --i) {
str[i] = (ch & 0x3f) | 0x80;
ch >>= 6;
}
str[0] = ch | first;
return len;
}
static const struct {
uint8_t mask;
uint8_t result;
int octets;
} sizes[] = {
{ 0x80, 0x00, 1 },
{ 0xE0, 0xC0, 2 },
{ 0xF0, 0xE0, 3 },
{ 0xF8, 0xF0, 4 },
{ 0xFC, 0xF8, 5 },
{ 0xFE, 0xF8, 6 },
{ 0x80, 0x80, -1 },
};
int utf8_size(const char *s) {
uint8_t c = (uint8_t)*s;
for (size_t i = 0; i < sizeof(sizes) / sizeof(*sizes); ++i) {
if ((c & sizes[i].mask) == sizes[i].result) {
return sizes[i].octets;
}
}
return -1;
}

View File

@ -0,0 +1,67 @@
<?xml version="1.0" encoding="UTF-8"?>
<protocol name="wlr_input_inhibit_unstable_v1">
<copyright>
Copyright © 2018 Drew DeVault
Permission to use, copy, modify, distribute, and sell this
software and its documentation for any purpose is hereby granted
without fee, provided that the above copyright notice appear in
all copies and that both that copyright notice and this permission
notice appear in supporting documentation, and that the name of
the copyright holders not be used in advertising or publicity
pertaining to distribution of the software without specific,
written prior permission. The copyright holders make no
representations about the suitability of this software for any
purpose. It is provided "as is" without express or implied
warranty.
THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.
</copyright>
<interface name="zwlr_input_inhibit_manager_v1" version="1">
<description summary="inhibits input events to other clients">
Clients can use this interface to prevent input events from being sent to
any surfaces but its own, which is useful for example in lock screen
software. It is assumed that access to this interface will be locked down
to whitelisted clients by the compositor.
</description>
<request name="get_inhibitor">
<description summary="inhibit input to other clients">
Activates the input inhibitor. As long as the inhibitor is active, the
compositor will not send input events to other clients.
</description>
<arg name="id" type="new_id" interface="zwlr_input_inhibitor_v1"/>
</request>
<enum name="error">
<entry name="already_inhibited" value="0" summary="an input inhibitor is already in use on the compositor"/>
</enum>
</interface>
<interface name="zwlr_input_inhibitor_v1" version="1">
<description summary="inhibits input to other clients">
While this resource exists, input to clients other than the owner of the
inhibitor resource will not receive input events. The client that owns
this resource will receive all input events normally. The compositor will
also disable all of its own input processing (such as keyboard shortcuts)
while the inhibitor is active.
The compositor may continue to send input events to selected clients,
such as an on-screen keyboard (via the input-method protocol).
</description>
<request name="destroy" type="destructor">
<description summary="destroy the input inhibitor object">
Destroy the inhibitor and allow other clients to receive input.
</description>
</request>
</interface>
</protocol>

View File

@ -0,0 +1,285 @@
<?xml version="1.0" encoding="UTF-8"?>
<protocol name="wlr_layer_shell_v1_unstable_v1">
<copyright>
Copyright © 2017 Drew DeVault
Permission to use, copy, modify, distribute, and sell this
software and its documentation for any purpose is hereby granted
without fee, provided that the above copyright notice appear in
all copies and that both that copyright notice and this permission
notice appear in supporting documentation, and that the name of
the copyright holders not be used in advertising or publicity
pertaining to distribution of the software without specific,
written prior permission. The copyright holders make no
representations about the suitability of this software for any
purpose. It is provided "as is" without express or implied
warranty.
THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.
</copyright>
<interface name="zwlr_layer_shell_v1" version="1">
<description summary="create surfaces that are layers of the desktop">
Clients can use this interface to assign the surface_layer role to
wl_surfaces. Such surfaces are assigned to a "layer" of the output and
rendered with a defined z-depth respective to each other. They may also be
anchored to the edges and corners of a screen and specify input handling
semantics. This interface should be suitable for the implementation of
many desktop shell components, and a broad number of other applications
that interact with the desktop.
</description>
<request name="get_layer_surface">
<description summary="create a layer_surface from a surface">
Create a layer surface for an existing surface. This assigns the role of
layer_surface, or raises a protocol error if another role is already
assigned.
Creating a layer surface from a wl_surface which has a buffer attached
or committed is a client error, and any attempts by a client to attach
or manipulate a buffer prior to the first layer_surface.configure call
must also be treated as errors.
You may pass NULL for output to allow the compositor to decide which
output to use. Generally this will be the one that the user most
recently interacted with.
Clients can specify a namespace that defines the purpose of the layer
surface.
</description>
<arg name="id" type="new_id" interface="zwlr_layer_surface_v1"/>
<arg name="surface" type="object" interface="wl_surface"/>
<arg name="output" type="object" interface="wl_output" allow-null="true"/>
<arg name="layer" type="uint" enum="layer" summary="layer to add this surface to"/>
<arg name="namespace" type="string" summary="namespace for the layer surface"/>
</request>
<enum name="error">
<entry name="role" value="0" summary="wl_surface has another role"/>
<entry name="invalid_layer" value="1" summary="layer value is invalid"/>
<entry name="already_constructed" value="2" summary="wl_surface has a buffer attached or committed"/>
</enum>
<enum name="layer">
<description summary="available layers for surfaces">
These values indicate which layers a surface can be rendered in. They
are ordered by z depth, bottom-most first. Traditional shell surfaces
will typically be rendered between the bottom and top layers.
Fullscreen shell surfaces are typically rendered at the top layer.
Multiple surfaces can share a single layer, and ordering within a
single layer is undefined.
</description>
<entry name="background" value="0"/>
<entry name="bottom" value="1"/>
<entry name="top" value="2"/>
<entry name="overlay" value="3"/>
</enum>
</interface>
<interface name="zwlr_layer_surface_v1" version="1">
<description summary="layer metadata interface">
An interface that may be implemented by a wl_surface, for surfaces that
are designed to be rendered as a layer of a stacked desktop-like
environment.
Layer surface state (size, anchor, exclusive zone, margin, interactivity)
is double-buffered, and will be applied at the time wl_surface.commit of
the corresponding wl_surface is called.
</description>
<request name="set_size">
<description summary="sets the size of the surface">
Sets the size of the surface in surface-local coordinates. The
compositor will display the surface centered with respect to its
anchors.
If you pass 0 for either value, the compositor will assign it and
inform you of the assignment in the configure event. You must set your
anchor to opposite edges in the dimensions you omit; not doing so is a
protocol error. Both values are 0 by default.
Size is double-buffered, see wl_surface.commit.
</description>
<arg name="width" type="uint"/>
<arg name="height" type="uint"/>
</request>
<request name="set_anchor">
<description summary="configures the anchor point of the surface">
Requests that the compositor anchor the surface to the specified edges
and corners. If two orthoginal edges are specified (e.g. 'top' and
'left'), then the anchor point will be the intersection of the edges
(e.g. the top left corner of the output); otherwise the anchor point
will be centered on that edge, or in the center if none is specified.
Anchor is double-buffered, see wl_surface.commit.
</description>
<arg name="anchor" type="uint" enum="anchor"/>
</request>
<request name="set_exclusive_zone">
<description summary="configures the exclusive geometry of this surface">
Requests that the compositor avoids occluding an area of the surface
with other surfaces. The compositor's use of this information is
implementation-dependent - do not assume that this region will not
actually be occluded.
A positive value is only meaningful if the surface is anchored to an
edge, rather than a corner. The zone is the number of surface-local
coordinates from the edge that are considered exclusive.
Surfaces that do not wish to have an exclusive zone may instead specify
how they should interact with surfaces that do. If set to zero, the
surface indicates that it would like to be moved to avoid occluding
surfaces with a positive excluzive zone. If set to -1, the surface
indicates that it would not like to be moved to accomodate for other
surfaces, and the compositor should extend it all the way to the edges
it is anchored to.
For example, a panel might set its exclusive zone to 10, so that
maximized shell surfaces are not shown on top of it. A notification
might set its exclusive zone to 0, so that it is moved to avoid
occluding the panel, but shell surfaces are shown underneath it. A
wallpaper or lock screen might set their exclusive zone to -1, so that
they stretch below or over the panel.
The default value is 0.
Exclusive zone is double-buffered, see wl_surface.commit.
</description>
<arg name="zone" type="int"/>
</request>
<request name="set_margin">
<description summary="sets a margin from the anchor point">
Requests that the surface be placed some distance away from the anchor
point on the output, in surface-local coordinates. Setting this value
for edges you are not anchored to has no effect.
The exclusive zone includes the margin.
Margin is double-buffered, see wl_surface.commit.
</description>
<arg name="top" type="int"/>
<arg name="right" type="int"/>
<arg name="bottom" type="int"/>
<arg name="left" type="int"/>
</request>
<request name="set_keyboard_interactivity">
<description summary="requests keyboard events">
Set to 1 to request that the seat send keyboard events to this layer
surface. For layers below the shell surface layer, the seat will use
normal focus semantics. For layers above the shell surface layers, the
seat will always give exclusive keyboard focus to the top-most layer
which has keyboard interactivity set to true.
Layer surfaces receive pointer, touch, and tablet events normally. If
you do not want to receive them, set the input region on your surface
to an empty region.
Events is double-buffered, see wl_surface.commit.
</description>
<arg name="keyboard_interactivity" type="uint"/>
</request>
<request name="get_popup">
<description summary="assign this layer_surface as an xdg_popup parent">
This assigns an xdg_popup's parent to this layer_surface. This popup
should have been created via xdg_surface::get_popup with the parent set
to NULL, and this request must be invoked before committing the popup's
initial state.
See the documentation of xdg_popup for more details about what an
xdg_popup is and how it is used.
</description>
<arg name="popup" type="object" interface="xdg_popup"/>
</request>
<request name="ack_configure">
<description summary="ack a configure event">
When a configure event is received, if a client commits the
surface in response to the configure event, then the client
must make an ack_configure request sometime before the commit
request, passing along the serial of the configure event.
If the client receives multiple configure events before it
can respond to one, it only has to ack the last configure event.
A client is not required to commit immediately after sending
an ack_configure request - it may even ack_configure several times
before its next surface commit.
A client may send multiple ack_configure requests before committing, but
only the last request sent before a commit indicates which configure
event the client really is responding to.
</description>
<arg name="serial" type="uint" summary="the serial from the configure event"/>
</request>
<request name="destroy" type="destructor">
<description summary="destroy the layer_surface">
This request destroys the layer surface.
</description>
</request>
<event name="configure">
<description summary="suggest a surface change">
The configure event asks the client to resize its surface.
Clients should arrange their surface for the new states, and then send
an ack_configure request with the serial sent in this configure event at
some point before committing the new surface.
The client is free to dismiss all but the last configure event it
received.
The width and height arguments specify the size of the window in
surface-local coordinates.
The size is a hint, in the sense that the client is free to ignore it if
it doesn't resize, pick a smaller size (to satisfy aspect ratio or
resize in steps of NxM pixels). If the client picks a smaller size and
is anchored to two opposite anchors (e.g. 'top' and 'bottom'), the
surface will be centered on this axis.
If the width or height arguments are zero, it means the client should
decide its own window dimension.
</description>
<arg name="serial" type="uint"/>
<arg name="width" type="uint"/>
<arg name="height" type="uint"/>
</event>
<event name="closed">
<description summary="surface should be closed">
The closed event is sent by the compositor when the surface will no
longer be shown. The output may have been destroyed or the user may
have asked for it to be removed. Further changes to the surface will be
ignored. The client should destroy the resource after receiving this
event, and create a new surface if they so choose.
</description>
</event>
<enum name="error">
<entry name="invalid_surface_state" value="0" summary="provided surface state is invalid"/>
<entry name="invalid_size" value="1" summary="size is invalid"/>
<entry name="invalid_anchor" value="2" summary="anchor bitfield is invalid"/>
</enum>
<enum name="anchor" bitfield="true">
<entry name="top" value="1" summary="the top edge of the anchor rectangle"/>
<entry name="bottom" value="2" summary="the bottom edge of the anchor rectangle"/>
<entry name="left" value="4" summary="the left edge of the anchor rectangle"/>
<entry name="right" value="8" summary="the right edge of the anchor rectangle"/>
</enum>
</interface>
</protocol>