From d053acbed6fea0f73eb79ac800c1342f8afadeb8 Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Tue, 3 Apr 2018 15:04:31 -0400 Subject: [PATCH] R E N D E R I N G --- common/background-image.c | 3 +- include/swaylock/swaylock.h | 10 +++ swaylock/main.c | 14 ++++- swaylock/password.c | 20 ++++-- swaylock/render.c | 121 +++++++++++++++++++++++++++++++++++- 5 files changed, 159 insertions(+), 9 deletions(-) diff --git a/common/background-image.c b/common/background-image.c index 1a6c0df0..e5fb4433 100644 --- a/common/background-image.c +++ b/common/background-image.c @@ -28,7 +28,8 @@ cairo_surface_t *load_background_image(const char *path) { GError *err = NULL; GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(path, &err); if (!pixbuf) { - wlr_log(L_ERROR, "Failed to load background image."); + wlr_log(L_ERROR, "Failed to load background image (%s).", + err->message); return false; } image = gdk_cairo_image_surface_create_from_pixbuf(pixbuf); diff --git a/include/swaylock/swaylock.h b/include/swaylock/swaylock.h index f3b0b58b..ddca633d 100644 --- a/include/swaylock/swaylock.h +++ b/include/swaylock/swaylock.h @@ -9,6 +9,14 @@ #include "swaylock/seat.h" #include "wlr-layer-shell-unstable-v1-client-protocol.h" +enum auth_state { + AUTH_STATE_IDLE, + AUTH_STATE_INPUT, + AUTH_STATE_BACKSPACE, + AUTH_STATE_VALIDATING, + AUTH_STATE_INVALID, +}; + struct swaylock_args { uint32_t color; enum background_mode mode; @@ -30,6 +38,7 @@ struct swaylock_state { struct swaylock_args args; struct swaylock_password password; struct swaylock_xkb xkb; + enum auth_state auth_state; bool run_display; }; @@ -48,5 +57,6 @@ struct swaylock_surface { 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); #endif diff --git a/swaylock/main.c b/swaylock/main.c index c8fdc2f4..ce337e24 100644 --- a/swaylock/main.c +++ b/swaylock/main.c @@ -133,6 +133,7 @@ int main(int argc, char **argv) { .color = 0xFFFFFFFF, .show_indicator = true, }; + cairo_surface_t *background_image = NULL; state.args = args; wlr_log_init(L_DEBUG, NULL); @@ -150,8 +151,13 @@ int main(int argc, char **argv) { break; } case 'i': - // TODO - return 1; + // TODO: Multiple background images (bleh) + background_image = load_background_image(optarg); + if (!background_image) { + return 1; + } + state.args.mode = BACKGROUND_MODE_FILL; + break; case 's': state.args.mode = parse_background_mode(optarg); if (state.args.mode == BACKGROUND_MODE_INVALID) { @@ -159,7 +165,7 @@ int main(int argc, char **argv) { } break; case 't': - // TODO + state.args.mode = BACKGROUND_MODE_TILE; break; case 'v': #if defined SWAY_GIT_VERSION && defined SWAY_GIT_BRANCH && defined SWAY_VERSION_DATE @@ -197,6 +203,8 @@ int main(int argc, char **argv) { struct swaylock_surface *surface; wl_list_for_each(surface, &state.surfaces, link) { + surface->image = background_image; + assert(surface->surface = wl_compositor_create_surface(state.compositor)); diff --git a/swaylock/password.c b/swaylock/password.c index 9af7fe16..2bdf151f 100644 --- a/swaylock/password.c +++ b/swaylock/password.c @@ -50,21 +50,23 @@ static bool attempt_password(struct swaylock_password *pw) { wlr_log(L_ERROR, "pam_end failed"); goto fail; } - // PAM freed this + // PAM frees this pw->buffer = NULL; pw->len = pw->size = 0; return true; fail: - // PAM freed this + // PAM frees this pw->buffer = NULL; pw->len = pw->size = 0; return false; } -static void backspace(struct swaylock_password *pw) { +static bool backspace(struct swaylock_password *pw) { if (pw->len != 0) { pw->buffer[--pw->len] = 0; + return true; } + return false; } static void append_ch(struct swaylock_password *pw, uint32_t codepoint) { @@ -97,17 +99,27 @@ void swaylock_handle_key(struct swaylock_state *state, switch (keysym) { case XKB_KEY_KP_Enter: /* fallthrough */ case XKB_KEY_Return: + state->auth_state = AUTH_STATE_VALIDATING; + render_frames(state); if (attempt_password(&state->password)) { exit(0); } + state->auth_state = AUTH_STATE_INVALID; + render_frames(state); break; case XKB_KEY_BackSpace: - backspace(&state->password); + if (backspace(&state->password)) { + state->auth_state = AUTH_STATE_BACKSPACE; + render_frames(state); + } break; default: if (codepoint) { append_ch(&state->password, codepoint); + state->auth_state = AUTH_STATE_INPUT; + render_frames(state); } break; } + // TODO: Expire state in a few seconds } diff --git a/swaylock/render.c b/swaylock/render.c index 8fc47281..90db71e3 100644 --- a/swaylock/render.c +++ b/swaylock/render.c @@ -1,21 +1,140 @@ #include +#include #include "cairo.h" #include "background-image.h" #include "swaylock/swaylock.h" +#define M_PI 3.14159265358979323846 + 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; + cairo_identity_matrix(cairo); if (state->args.mode == BACKGROUND_MODE_SOLID_COLOR) { cairo_set_source_u32(cairo, state->args.color); cairo_paint(cairo); } else { + // TODO: hidpi render_background_image(cairo, surface->image, - state->args.mode, surface->width, surface->height); + state->args.mode, surface->width, surface->height, 1); + } + cairo_identity_matrix(cairo); + + const int ARC_RADIUS = 50; + const int ARC_THICKNESS = 10; + const float TYPE_INDICATOR_RANGE = M_PI / 3.0f; + const float TYPE_INDICATOR_BORDER_THICKNESS = M_PI / 128.0f; + if (state->args.show_indicator && state->auth_state != AUTH_STATE_IDLE) { + // Draw circle + cairo_set_line_width(cairo, ARC_THICKNESS); + cairo_arc(cairo, surface->width / 2, surface->height / 2, + ARC_RADIUS, 0, 2 * M_PI); + switch (state->auth_state) { + case AUTH_STATE_INPUT: + case AUTH_STATE_BACKSPACE: { + cairo_set_source_rgba(cairo, 0, 0, 0, 0.75); + cairo_fill_preserve(cairo); + cairo_set_source_rgb(cairo, 51.0 / 255, 125.0 / 255, 0); + cairo_stroke(cairo); + } break; + case AUTH_STATE_VALIDATING: { + cairo_set_source_rgba(cairo, 0, 114.0 / 255, 255.0 / 255, 0.75); + cairo_fill_preserve(cairo); + cairo_set_source_rgb(cairo, 51.0 / 255, 0, 250.0 / 255); + cairo_stroke(cairo); + } break; + case AUTH_STATE_INVALID: { + cairo_set_source_rgba(cairo, 250.0 / 255, 0, 0, 0.75); + cairo_fill_preserve(cairo); + cairo_set_source_rgb(cairo, 125.0 / 255, 51.0 / 255, 0); + cairo_stroke(cairo); + } break; + default: break; + } + + // Draw a message + char *text = NULL; + cairo_set_source_rgb(cairo, 0, 0, 0); + cairo_select_font_face(cairo, "sans-serif", + CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL); + cairo_set_font_size(cairo, ARC_RADIUS / 3.0f); + switch (state->auth_state) { + case AUTH_STATE_VALIDATING: + text = "verifying"; + break; + case AUTH_STATE_INVALID: + text = "wrong"; + break; + default: break; + } + + if (text) { + cairo_text_extents_t extents; + double x, y; + cairo_text_extents(cairo, text, &extents); + x = (surface->width / 2) - + (extents.width / 2 + extents.x_bearing); + y = (surface->height / 2) - + (extents.height / 2 + extents.y_bearing); + + cairo_move_to(cairo, x, y); + cairo_show_text(cairo, text); + cairo_close_path(cairo); + cairo_new_sub_path(cairo); + } + + // Typing indicator: Highlight random part on keypress + if (state->auth_state == AUTH_STATE_INPUT + || state->auth_state == AUTH_STATE_BACKSPACE) { + static double highlight_start = 0; + highlight_start += + (rand() % (int)(M_PI * 100)) / 100.0 + M_PI * 0.5; + cairo_arc(cairo, surface->width / 2, surface->height / 2, + ARC_RADIUS, highlight_start, + highlight_start + TYPE_INDICATOR_RANGE); + if (state->auth_state == AUTH_STATE_INPUT) { + cairo_set_source_rgb(cairo, 51.0 / 255, 219.0 / 255, 0); + } else { + cairo_set_source_rgb(cairo, 219.0 / 255, 51.0 / 255, 0); + } + cairo_stroke(cairo); + + // Draw borders + cairo_set_source_rgb(cairo, 0, 0, 0); + cairo_arc(cairo, surface->width / 2, surface->height / 2, + ARC_RADIUS, highlight_start, + highlight_start + TYPE_INDICATOR_BORDER_THICKNESS); + cairo_stroke(cairo); + + cairo_arc(cairo, surface->width / 2, surface->height / 2, + ARC_RADIUS, highlight_start + TYPE_INDICATOR_RANGE, + highlight_start + TYPE_INDICATOR_RANGE + + TYPE_INDICATOR_BORDER_THICKNESS); + cairo_stroke(cairo); + } + + // Draw inner + outer border of the circle + cairo_set_source_rgb(cairo, 0, 0, 0); + cairo_set_line_width(cairo, 2.0); + cairo_arc(cairo, surface->width / 2, surface->height / 2, + ARC_RADIUS - ARC_THICKNESS / 2, 0, 2 * M_PI); + cairo_stroke(cairo); + cairo_arc(cairo, surface->width / 2, surface->height / 2, + ARC_RADIUS + ARC_THICKNESS / 2, 0, 2 * M_PI); + cairo_stroke(cairo); } + 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); + wl_display_roundtrip(state->display); +} + +void render_frames(struct swaylock_state *state) { + struct swaylock_surface *surface; + wl_list_for_each(surface, &state->surfaces, link) { + render_frame(surface); + } }