From b90637e2a6e854d586b977ed9b80b806edeb12dd Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Sun, 13 Jan 2019 21:15:32 -0500 Subject: [PATCH] Convert swaylock into standalone project --- .gitignore | 1 + background-image.c | 121 ++++++++++++ cairo.c | 143 ++++++++++++++ include/background-image.h | 20 ++ include/cairo.h | 25 +++ include/list.h | 35 ++++ include/loop.h | 54 ++++++ include/meson.build | 1 + include/pool-buffer.h | 24 +++ include/seat.h | 21 ++ include/swaylock.h | 112 +++++++++++ include/unicode.h | 33 ++++ list.c | 156 +++++++++++++++ loop.c | 178 +++++++++++++++++ main.c | 44 ++++- meson.build | 138 +++++++++++++- meson_options.txt | 5 + pam.c | 2 +- pango.c | 133 +++++++++++++ password.c | 4 +- pool-buffer.c | 148 +++++++++++++++ render.c | 2 +- seat.c | 4 +- unicode.c | 101 ++++++++++ wlr-input-inhibitor-unstable-v1.xml | 67 +++++++ wlr-layer-shell-unstable-v1.xml | 285 ++++++++++++++++++++++++++++ 26 files changed, 1838 insertions(+), 19 deletions(-) create mode 100644 .gitignore create mode 100644 background-image.c create mode 100644 cairo.c create mode 100644 include/background-image.h create mode 100644 include/cairo.h create mode 100644 include/list.h create mode 100644 include/loop.h create mode 100644 include/meson.build create mode 100644 include/pool-buffer.h create mode 100644 include/seat.h create mode 100644 include/swaylock.h create mode 100644 include/unicode.h create mode 100644 list.c create mode 100644 loop.c create mode 100644 meson_options.txt create mode 100644 pango.c create mode 100644 pool-buffer.c create mode 100644 unicode.c create mode 100644 wlr-input-inhibitor-unstable-v1.xml create mode 100644 wlr-layer-shell-unstable-v1.xml diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..378eac2 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +build diff --git a/background-image.c b/background-image.c new file mode 100644 index 0000000..72f39a7 --- /dev/null +++ b/background-image.c @@ -0,0 +1,121 @@ +#include +#include +#include +#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); +} diff --git a/cairo.c b/cairo.c new file mode 100644 index 0000000..f2ad54c --- /dev/null +++ b/cairo.c @@ -0,0 +1,143 @@ +#include +#include +#include "cairo.h" +#if HAVE_GDK_PIXBUF +#include +#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 diff --git a/include/background-image.h b/include/background-image.h new file mode 100644 index 0000000..15935ff --- /dev/null +++ b/include/background-image.h @@ -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 diff --git a/include/cairo.h b/include/cairo.h new file mode 100644 index 0000000..15664cd --- /dev/null +++ b/include/cairo.h @@ -0,0 +1,25 @@ +#ifndef _SWAY_CAIRO_H +#define _SWAY_CAIRO_H + +#include "config.h" +#include +#include +#include +#if HAVE_GDK_PIXBUF +#include +#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 diff --git a/include/list.h b/include/list.h new file mode 100644 index 0000000..895f6cc --- /dev/null +++ b/include/list.h @@ -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 diff --git a/include/loop.h b/include/loop.h new file mode 100644 index 0000000..2f608ed --- /dev/null +++ b/include/loop.h @@ -0,0 +1,54 @@ +#ifndef _SWAY_LOOP_H +#define _SWAY_LOOP_H +#include + +/** + * 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 diff --git a/include/meson.build b/include/meson.build new file mode 100644 index 0000000..65ed027 --- /dev/null +++ b/include/meson.build @@ -0,0 +1 @@ +configure_file(output: 'config.h', configuration: conf_data) diff --git a/include/pool-buffer.h b/include/pool-buffer.h new file mode 100644 index 0000000..54f5be0 --- /dev/null +++ b/include/pool-buffer.h @@ -0,0 +1,24 @@ +#ifndef _SWAY_BUFFERS_H +#define _SWAY_BUFFERS_H +#include +#include +#include +#include +#include + +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 diff --git a/include/seat.h b/include/seat.h new file mode 100644 index 0000000..c79afcd --- /dev/null +++ b/include/seat.h @@ -0,0 +1,21 @@ +#ifndef _SWAYLOCK_SEAT_H +#define _SWAYLOCK_SEAT_H +#include + +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 diff --git a/include/swaylock.h b/include/swaylock.h new file mode 100644 index 0000000..f089a54 --- /dev/null +++ b/include/swaylock.h @@ -0,0 +1,112 @@ +#ifndef _SWAYLOCK_H +#define _SWAYLOCK_H +#include +#include +#include +#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 diff --git a/include/unicode.h b/include/unicode.h new file mode 100644 index 0000000..e2ee958 --- /dev/null +++ b/include/unicode.h @@ -0,0 +1,33 @@ +#ifndef _SWAY_UNICODE_H +#define _SWAY_UNICODE_H +#include +#include + +// 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 + diff --git a/list.c b/list.c new file mode 100644 index 0000000..2d0076d --- /dev/null +++ b/list.c @@ -0,0 +1,156 @@ +#include "list.h" +#include +#include +#include + +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); +} + diff --git a/loop.c b/loop.c new file mode 100644 index 0000000..cd7b8ce --- /dev/null +++ b/loop.c @@ -0,0 +1,178 @@ +#define _POSIX_C_SOURCE 200112L +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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; +} diff --git a/main.c b/main.c index 9a4f3b5..f0b8d9d 100644 --- a/main.c +++ b/main.c @@ -16,15 +16,12 @@ #include #include #include -#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) { diff --git a/meson.build b/meson.build index f3321a7..71d2f78 100644 --- a/meson.build +++ b/meson.build @@ -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 ) diff --git a/meson_options.txt b/meson_options.txt new file mode 100644 index 0000000..60268b9 --- /dev/null +++ b/meson_options.txt @@ -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') diff --git a/pam.c b/pam.c index b90d9e8..e48056f 100644 --- a/pam.c +++ b/pam.c @@ -6,7 +6,7 @@ #include #include #include -#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 diff --git a/pango.c b/pango.c new file mode 100644 index 0000000..a3ed543 --- /dev/null +++ b/pango.c @@ -0,0 +1,133 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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, "&"); + break; + case '<': + length += 4; + lenient_strcat(dest, "<"); + break; + case '>': + length += 4; + lenient_strcat(dest, ">"); + break; + case '\'': + length += 6; + lenient_strcat(dest, "'"); + break; + case '"': + length += 6; + lenient_strcat(dest, """); + 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); +} diff --git a/password.c b/password.c index 3bd113a..5e69b3d 100644 --- a/password.c +++ b/password.c @@ -6,8 +6,8 @@ #include #include #include -#include "swaylock/swaylock.h" -#include "swaylock/seat.h" +#include "swaylock.h" +#include "seat.h" #include "loop.h" #include "unicode.h" diff --git a/pool-buffer.c b/pool-buffer.c new file mode 100644 index 0000000..2eadb4f --- /dev/null +++ b/pool-buffer.c @@ -0,0 +1,148 @@ +#define _POSIX_C_SOURCE 200809 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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; +} diff --git a/render.c b/render.c index cbd5d01..1e49b0b 100644 --- a/render.c +++ b/render.c @@ -3,7 +3,7 @@ #include #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; diff --git a/seat.c b/seat.c index f0b1385..2eb02fc 100644 --- a/seat.c +++ b/seat.c @@ -4,8 +4,8 @@ #include #include #include -#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) { diff --git a/unicode.c b/unicode.c new file mode 100644 index 0000000..5070e08 --- /dev/null +++ b/unicode.c @@ -0,0 +1,101 @@ +#include +#include +#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; +} diff --git a/wlr-input-inhibitor-unstable-v1.xml b/wlr-input-inhibitor-unstable-v1.xml new file mode 100644 index 0000000..b62d1bb --- /dev/null +++ b/wlr-input-inhibitor-unstable-v1.xml @@ -0,0 +1,67 @@ + + + + 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. + + + + + 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. + + + + + Activates the input inhibitor. As long as the inhibitor is active, the + compositor will not send input events 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). + + + + + Destroy the inhibitor and allow other clients to receive input. + + + + diff --git a/wlr-layer-shell-unstable-v1.xml b/wlr-layer-shell-unstable-v1.xml new file mode 100644 index 0000000..2bb72ed --- /dev/null +++ b/wlr-layer-shell-unstable-v1.xml @@ -0,0 +1,285 @@ + + + + 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. + + + + + 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. + + + + + 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. + + + + + + + + + + + + + + + + + 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. + + + + + + + + + + + + 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. + + + + + 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. + + + + + + + + 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. + + + + + + + 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. + + + + + + + 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. + + + + + + + + + + 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. + + + + + + + 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. + + + + + + + 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. + + + + + + + This request destroys the layer surface. + + + + + + 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. + + + + + + + + + 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. + + + + + + + + + + + + + + + + +