From a6cc194e6e8a6df417c890589c54d1e6b1966875 Mon Sep 17 00:00:00 2001 From: James Magahern Date: Sun, 20 Jan 2019 19:05:51 -0800 Subject: Red flash animation on bad password --- src/animation.h | 29 ++++++++++++++++++----------- src/main.c | 36 +++++++++++++++++++++--------------- src/render.c | 53 +++++++++++++++++++++++++++++++++++++++++++++++------ src/render.h | 5 +++++ 4 files changed, 91 insertions(+), 32 deletions(-) diff --git a/src/animation.h b/src/animation.h index 3064d1b..a74bb60 100644 --- a/src/animation.h +++ b/src/animation.h @@ -13,40 +13,47 @@ typedef double anim_time_interval_t; struct animation_t; typedef void(*AnimationCompletion)(struct animation_t *anim, void *context); +typedef enum { + IN, + OUT +} AnimationDirection; + typedef enum { _EmptyAnimationType, ACursorAnimation, ALogoAnimation, + ARedFlashAnimation, } AnimationType; typedef struct { - AnimationType type; - bool cursor_animating; double cursor_fade_direction; } CursorAnimation; typedef struct { - AnimationType type; - - bool direction; // false: in, true: out + AnimationDirection direction; } LogoAnimation; -typedef union { - AnimationType type; +typedef struct { + AnimationDirection direction; + unsigned flash_count; +} RedFlashAnimation; - CursorAnimation cursor_anim; - LogoAnimation logo_anim; +typedef union { + CursorAnimation cursor_anim; + LogoAnimation logo_anim; + RedFlashAnimation redflash_anim; } Animation; typedef struct { + AnimationType type; + Animation anim; + bool completed; anim_time_interval_t start_time; AnimationCompletion completion_func; void *completion_func_context; - - Animation anim; } animation_t; // Convenience: returns current time as anim_time_interval_t diff --git a/src/main.c b/src/main.c index c70ff9d..ddaea43 100644 --- a/src/main.c +++ b/src/main.c @@ -18,8 +18,8 @@ static const int kXSecureLockCharFD = 0; static const size_t kMaxPasswordLength = 128; -static const int kDefaultWidth = 800; -static const int kDefaultHeight = 600; +static const int kDefaultWidth = 1024; +static const int kDefaultHeight = 768; static inline saver_state_t* saver_state(void *c) { @@ -175,17 +175,29 @@ static void ending_animation_completed(struct animation_t *animation, void *cont static void authentication_accepted(saver_state_t *state) { animation_t out_animation = { - .completed = false, + .type = ALogoAnimation, .completion_func = ending_animation_completed, .completion_func_context = state, .anim.logo_anim = { - .type = ALogoAnimation, .direction = true } }; schedule_animation(state, out_animation); } +static void authentication_rejected(saver_state_t *state) +{ + animation_t flash_animation = { + .type = ARedFlashAnimation, + .anim.redflash_anim = { + .direction = IN + } + }; + schedule_animation(state, flash_animation); + + clear_password(state); +} + /* * Auth callbacks */ @@ -219,7 +231,7 @@ void callback_authentication_result(int result, void *context) authentication_accepted(state); } else { // Try again - clear_password(state); + authentication_rejected(state); } } @@ -235,11 +247,7 @@ static void update(saver_state_t *state) static void draw(saver_state_t *state) { - // Draw background - cairo_t *cr = state->ctx; - cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 1.0); - cairo_paint(cr); - + draw_background(state); draw_logo(state); draw_password_field(state); } @@ -268,9 +276,8 @@ static int runloop(cairo_surface_t *surface) // Add initial animations // Cursor animation -- repeats indefinitely animation_t cursor_animation = { - .completed = false, + .type = ACursorAnimation, .anim.cursor_anim = { - .type = ACursorAnimation, .cursor_fade_direction = -1.0, .cursor_animating = true } @@ -279,10 +286,9 @@ static int runloop(cairo_surface_t *surface) // Logo incoming animation animation_t logo_animation = { - .completed = false, + .type = ALogoAnimation, .anim.logo_anim = { - .type = ALogoAnimation, - .direction = false + .direction = IN } }; schedule_animation(&state, logo_animation); diff --git a/src/render.c b/src/render.c index 34f5550..9f2c1b1 100644 --- a/src/render.c +++ b/src/render.c @@ -9,6 +9,7 @@ #include #include +#include static const double kLogoBackgroundWidth = 500.0; @@ -35,7 +36,7 @@ GBytes* get_data_for_resource(const char *resource_path) static void update_single_animation(saver_state_t *state, animation_t *anim) { // Cursor animation - if (anim->anim.type == ACursorAnimation) { + if (anim->type == ACursorAnimation) { CursorAnimation *ca = &anim->anim.cursor_anim; if (ca->cursor_animating && !state->is_processing) { @@ -57,7 +58,7 @@ static void update_single_animation(saver_state_t *state, animation_t *anim) } // Logo animation - else if (anim->anim.type == ALogoAnimation) { + else if (anim->type == ALogoAnimation) { const double logo_duration = 0.6; anim_time_interval_t now = anim_now(); @@ -79,6 +80,39 @@ static void update_single_animation(saver_state_t *state, animation_t *anim) anim->completed = completed; } + + // Background red flash animation + else if (anim->type == ARedFlashAnimation) { + const double duration = 0.1; + + anim_time_interval_t now = anim_now(); + double progress = (now - anim->start_time) / duration; + progress = anim_qubic_ease_out(progress); + + // Check for reverse direction + if (anim->anim.redflash_anim.direction == OUT) { + progress = 1.0 - progress; + } + + AnimationDirection direction = anim->anim.redflash_anim.direction; + bool finished = (progress >= 1.0); + if (anim->anim.redflash_anim.direction) { + finished = (progress <= 0.0); + } + + bool completed = false; + if (finished) { + anim->anim.redflash_anim.flash_count++; + anim->anim.redflash_anim.direction = !direction; + anim->start_time = anim_now(); + if (anim->anim.redflash_anim.flash_count > 3) { + completed = true; + } + } + + anim->completed = completed; + state->background_redshift = progress; + } } static unsigned next_anim_index(saver_state_t *state, unsigned cur_idx) @@ -86,7 +120,7 @@ static unsigned next_anim_index(saver_state_t *state, unsigned cur_idx) unsigned idx = cur_idx + 1; for (; idx < kMaxAnimations; idx++) { animation_t anim = state->animations[idx]; - if (anim.anim.type != _EmptyAnimationType) break; + if (anim.type != _EmptyAnimationType) break; } return idx; @@ -99,7 +133,7 @@ void schedule_animation(saver_state_t *state, animation_t anim) // Find next empty element for (unsigned idx = 0; idx < kMaxAnimations; idx++) { animation_t check_anim = state->animations[idx]; - if (check_anim.anim.type == _EmptyAnimationType) { + if (check_anim.type == _EmptyAnimationType) { state->animations[idx] = anim; state->num_animations++; break; @@ -117,7 +151,7 @@ void update_animations(saver_state_t *state) update_single_animation(state, anim); if (anim->completed) { - state->animations[idx].anim.type = _EmptyAnimationType; + state->animations[idx].type = _EmptyAnimationType; if (anim->completion_func != NULL) { anim->completion_func((struct animation_t *)anim, anim->completion_func_context); } @@ -133,6 +167,14 @@ void update_animations(saver_state_t *state) state->num_animations -= completed_animations; } +void draw_background(saver_state_t *state) +{ + // Draw background + cairo_t *cr = state->ctx; + cairo_set_source_rgba(cr, (state->background_redshift / 1.5), 0.0, 0.0, 1.0); + cairo_paint(cr); +} + void draw_logo(saver_state_t *state) { if (state->logo_svg_handle == NULL) { @@ -252,6 +294,5 @@ void draw_password_field(saver_state_t *state) cairo_rectangle(cr, field_x, field_y, cursor_offset_x, cursor_height); } cairo_fill(cr); - } diff --git a/src/render.h b/src/render.h index 4520b3e..137470d 100644 --- a/src/render.h +++ b/src/render.h @@ -24,6 +24,8 @@ typedef struct { PangoLayout *pango_layout; PangoFontDescription *status_font; + double background_redshift; + RsvgHandle *logo_svg_handle; double logo_fill_progress; @@ -55,6 +57,9 @@ void schedule_animation(saver_state_t *state, animation_t anim); // Update all running animations void update_animations(saver_state_t *state); +// Background +void draw_background(saver_state_t *state); + // The purple sidebar void draw_logo(saver_state_t *state); -- cgit v1.2.3