diff --git a/common/meson.build b/common/meson.build index 851e7bbf..44a29508 100644 --- a/common/meson.build +++ b/common/meson.build @@ -9,6 +9,7 @@ lib_sway_common = static_library( 'pango.c', 'readline.c', 'stringop.c', + 'unicode.c', 'util.c' ), dependencies: [ diff --git a/common/unicode.c b/common/unicode.c new file mode 100644 index 00000000..38a9b48e --- /dev/null +++ b/common/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) / 2; ++i) { + if ((c & sizes[i].mask) == sizes[i].result) { + return sizes[i].octets; + } + } + return -1; +} diff --git a/include/swaylock/swaylock.h b/include/swaylock/swaylock.h index e2673aae..f3b0b58b 100644 --- a/include/swaylock/swaylock.h +++ b/include/swaylock/swaylock.h @@ -15,18 +15,25 @@ struct swaylock_args { bool show_indicator; }; +struct swaylock_password { + size_t size; + size_t len; + char *buffer; +}; + struct swaylock_state { struct wl_display *display; struct wl_compositor *compositor; struct zwlr_layer_shell_v1 *layer_shell; struct wl_shm *shm; - struct wl_list contexts; + struct wl_list surfaces; struct swaylock_args args; + struct swaylock_password password; struct swaylock_xkb xkb; bool run_display; }; -struct swaylock_context { +struct swaylock_surface { cairo_surface_t *image; struct swaylock_state *state; struct wl_output *output; @@ -38,4 +45,8 @@ struct swaylock_context { 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); + #endif diff --git a/include/unicode.h b/include/unicode.h new file mode 100644 index 00000000..e2ee9588 --- /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/swaylock/main.c b/swaylock/main.c index 7602e47e..c8fdc2f4 100644 --- a/swaylock/main.c +++ b/swaylock/main.c @@ -32,39 +32,22 @@ static void daemonize() { } } -static void render_frame(struct swaylock_context *context) { - struct swaylock_state *state = context->state; - context->current_buffer = get_next_buffer(state->shm, - context->buffers, context->width, context->height); - cairo_t *cairo = context->current_buffer->cairo; - if (state->args.mode == BACKGROUND_MODE_SOLID_COLOR) { - cairo_set_source_u32(cairo, state->args.color); - cairo_paint(cairo); - } else { - render_background_image(cairo, context->image, - state->args.mode, context->width, context->height); - } - wl_surface_attach(context->surface, context->current_buffer->buffer, 0, 0); - wl_surface_damage(context->surface, 0, 0, context->width, context->height); - wl_surface_commit(context->surface); -} - static void layer_surface_configure(void *data, - struct zwlr_layer_surface_v1 *surface, + struct zwlr_layer_surface_v1 *layer_surface, uint32_t serial, uint32_t width, uint32_t height) { - struct swaylock_context *context = data; - context->width = width; - context->height = height; - zwlr_layer_surface_v1_ack_configure(surface, serial); - render_frame(context); + struct swaylock_surface *surface = data; + surface->width = width; + surface->height = height; + zwlr_layer_surface_v1_ack_configure(layer_surface, serial); + render_frame(surface); } static void layer_surface_closed(void *data, - struct zwlr_layer_surface_v1 *surface) { - struct swaylock_context *context = data; - zwlr_layer_surface_v1_destroy(context->layer_surface); - wl_surface_destroy(context->surface); - context->state->run_display = false; + struct zwlr_layer_surface_v1 *layer_surface) { + struct swaylock_surface *surface = data; + zwlr_layer_surface_v1_destroy(surface->layer_surface); + wl_surface_destroy(surface->surface); + surface->state->run_display = false; } static const struct zwlr_layer_surface_v1_listener layer_surface_listener = { @@ -89,12 +72,12 @@ static void handle_global(void *data, struct wl_registry *registry, state->layer_shell = wl_registry_bind( registry, name, &zwlr_layer_shell_v1_interface, 1); } else if (strcmp(interface, wl_output_interface.name) == 0) { - struct swaylock_context *context = - calloc(1, sizeof(struct swaylock_context)); - context->state = state; - context->output = wl_registry_bind(registry, name, + struct swaylock_surface *surface = + calloc(1, sizeof(struct swaylock_surface)); + surface->state = state; + surface->output = wl_registry_bind(registry, name, &wl_output_interface, 1); - wl_list_insert(&state->contexts, &context->link); + wl_list_insert(&state->surfaces, &surface->link); } } @@ -198,7 +181,7 @@ int main(int argc, char **argv) { } } - wl_list_init(&state.contexts); + wl_list_init(&state.surfaces); state.xkb.context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); assert(state.display = wl_display_connect(NULL)); @@ -207,33 +190,33 @@ int main(int argc, char **argv) { wl_display_roundtrip(state.display); assert(state.compositor && state.layer_shell && state.shm); - if (wl_list_empty(&state.contexts)) { + if (wl_list_empty(&state.surfaces)) { wlr_log(L_DEBUG, "Exiting - no outputs to show on."); return 0; } - struct swaylock_context *context; - wl_list_for_each(context, &state.contexts, link) { - assert(context->surface = + struct swaylock_surface *surface; + wl_list_for_each(surface, &state.surfaces, link) { + assert(surface->surface = wl_compositor_create_surface(state.compositor)); - context->layer_surface = zwlr_layer_shell_v1_get_layer_surface( - state.layer_shell, context->surface, context->output, + surface->layer_surface = zwlr_layer_shell_v1_get_layer_surface( + state.layer_shell, surface->surface, surface->output, ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY, "lockscreen"); - assert(context->layer_surface); + assert(surface->layer_surface); - zwlr_layer_surface_v1_set_size(context->layer_surface, 0, 0); - zwlr_layer_surface_v1_set_anchor(context->layer_surface, + zwlr_layer_surface_v1_set_size(surface->layer_surface, 0, 0); + zwlr_layer_surface_v1_set_anchor(surface->layer_surface, ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT); - zwlr_layer_surface_v1_set_exclusive_zone(context->layer_surface, -1); + zwlr_layer_surface_v1_set_exclusive_zone(surface->layer_surface, -1); zwlr_layer_surface_v1_set_keyboard_interactivity( - context->layer_surface, true); - zwlr_layer_surface_v1_add_listener(context->layer_surface, - &layer_surface_listener, context); - wl_surface_commit(context->surface); + surface->layer_surface, true); + zwlr_layer_surface_v1_add_listener(surface->layer_surface, + &layer_surface_listener, surface); + wl_surface_commit(surface->surface); wl_display_roundtrip(state.display); } diff --git a/swaylock/meson.build b/swaylock/meson.build index 2a1f029a..3cde47a4 100644 --- a/swaylock/meson.build +++ b/swaylock/meson.build @@ -1,6 +1,8 @@ executable( 'swaylock', [ 'main.c', + 'password.c', + 'render.c', 'seat.c' ], include_directories: [sway_inc], diff --git a/swaylock/password.c b/swaylock/password.c new file mode 100644 index 00000000..da67205d --- /dev/null +++ b/swaylock/password.c @@ -0,0 +1,57 @@ +#include +#include +#include +#include +#include +#include "swaylock/swaylock.h" +#include "swaylock/seat.h" +#include "unicode.h" + +static void backspace(struct swaylock_password *pw) { + if (pw->len != 0) { + pw->buffer[--pw->len] = 0; + } +} + +static void append_ch(struct swaylock_password *pw, uint32_t codepoint) { + if (!pw->buffer) { + pw->size = 8; + if (!(pw->buffer = malloc(pw->size))) { + // TODO: Display error + return; + } + pw->buffer[0] = 0; + } + size_t utf8_size = utf8_chsize(codepoint); + if (pw->len + utf8_size + 1 >= pw->size) { + size_t size = pw->size * 2; + char *buffer = realloc(pw->buffer, size); + if (!buffer) { + // TODO: Display error + return; + } + pw->size = size; + pw->buffer = buffer; + } + utf8_encode(&pw->buffer[pw->len], codepoint); + pw->buffer[pw->len + utf8_size] = 0; + pw->len += utf8_size; +} + +void swaylock_handle_key(struct swaylock_state *state, + xkb_keysym_t keysym, uint32_t codepoint) { + switch (keysym) { + case XKB_KEY_KP_Enter: /* fallthrough */ + case XKB_KEY_Return: + // TODO: Attempt password + break; + case XKB_KEY_BackSpace: + backspace(&state->password); + break; + default: + if (codepoint) { + append_ch(&state->password, codepoint); + } + break; + } +} diff --git a/swaylock/render.c b/swaylock/render.c new file mode 100644 index 00000000..8fc47281 --- /dev/null +++ b/swaylock/render.c @@ -0,0 +1,21 @@ +#include +#include "cairo.h" +#include "background-image.h" +#include "swaylock/swaylock.h" + +void render_frame(struct swaylock_surface *surface) { + struct swaylock_state *state = surface->state; + surface->current_buffer = get_next_buffer(state->shm, + surface->buffers, surface->width, surface->height); + cairo_t *cairo = surface->current_buffer->cairo; + if (state->args.mode == BACKGROUND_MODE_SOLID_COLOR) { + cairo_set_source_u32(cairo, state->args.color); + cairo_paint(cairo); + } else { + render_background_image(cairo, surface->image, + state->args.mode, surface->width, surface->height); + } + wl_surface_attach(surface->surface, surface->current_buffer->buffer, 0, 0); + wl_surface_damage(surface->surface, 0, 0, surface->width, surface->height); + wl_surface_commit(surface->surface); +} diff --git a/swaylock/seat.c b/swaylock/seat.c index 522200f2..6c46bb41 100644 --- a/swaylock/seat.c +++ b/swaylock/seat.c @@ -73,7 +73,9 @@ static void keyboard_key(void *data, struct wl_keyboard *wl_keyboard, uint32_t keycode = key_state == WL_KEYBOARD_KEY_STATE_PRESSED ? key + 8 : 0; uint32_t codepoint = xkb_state_key_get_utf32(state->xkb.state, keycode); - wlr_log(L_DEBUG, "%c %d", codepoint, sym); + if (key_state == WL_KEYBOARD_KEY_STATE_PRESSED) { + swaylock_handle_key(state, sym, codepoint); + } } static void keyboard_modifiers(void *data, struct wl_keyboard *wl_keyboard,