Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add a screensaver #621

Merged
merged 5 commits into from Oct 12, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/CMakeLists.txt
Expand Up @@ -98,6 +98,7 @@ set(DBB-FIRMWARE-UI-SOURCES
${CMAKE_SOURCE_DIR}/src/ui/fonts/monogram_5X9.c
${CMAKE_SOURCE_DIR}/src/ui/fonts/password_9X9.c
${CMAKE_SOURCE_DIR}/src/ui/fonts/password_11X12.c
${CMAKE_SOURCE_DIR}/src/ui/screen_saver.c
${CMAKE_SOURCE_DIR}/src/ui/screen_stack.c
${CMAKE_SOURCE_DIR}/src/ui/screen_process.c
${CMAKE_SOURCE_DIR}/src/ui/event_handler.c
Expand All @@ -106,6 +107,7 @@ set(DBB-FIRMWARE-UI-SOURCES
${CMAKE_SOURCE_DIR}/src/ui/components/trinary_input_char.c
${CMAKE_SOURCE_DIR}/src/ui/components/trinary_input_string.c
${CMAKE_SOURCE_DIR}/src/ui/components/waiting.c
${CMAKE_SOURCE_DIR}/src/ui/components/screensaver.c
${CMAKE_SOURCE_DIR}/src/ui/components/entry_screen.c
${CMAKE_SOURCE_DIR}/src/ui/components/knight_rider.c
${CMAKE_SOURCE_DIR}/src/ui/components/right_arrow.c
Expand Down
106 changes: 106 additions & 0 deletions src/ui/components/screensaver.c
@@ -0,0 +1,106 @@
// Copyright 2020 Shift Crypto AG
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "screensaver.h"

#include "image.h"
#include "ui_images.h"

#include <hardfault.h>
#include <screen.h>
#include <ui/ui_util.h>

#include <stdint.h>
#include <string.h>

static void _render(component_t* component)
{
component_t* image = component->sub_components.sub_components[0];

// The counter is used to slow down the animation, see the slowdown vars below.
static uint16_t counter = 0;
benma marked this conversation as resolved.
Show resolved Hide resolved
counter++;
benma marked this conversation as resolved.
Show resolved Hide resolved

// these flip between positive and negative when boncing, can also be used to move multiple
// pixels per frame
static int8_t x_direction = 1;
static int8_t y_direction = 1;
// setting relative speed for both axes
const int8_t x_slowdown = 6;
const int8_t y_slowdown = 6;

if (counter % x_slowdown == 0) {
image->position.left += x_direction;
// if the screensaver is at the edge (or outside e.g. due to screensaver_reset), and moving
// away from the screen, flip the direction so it will always be moving inside or towards
// the screen
if ((x_direction > 0 &&
(image->position.left + image->dimension.width) >= component->dimension.width) ||
(x_direction < 0 && image->position.left < 0)) {
x_direction *= -1;
}
}
if (counter % y_slowdown == 0) {
image->position.top += y_direction;
// if the screensaver is at the edge (or outside e.g. due to screensaver_reset), and moving
// away from the screen, flip the direction so it will always be moving inside or towards
// the screen
if ((y_direction > 0 &&
(image->position.top + image->dimension.height) >= component->dimension.height) ||
(y_direction < 0 && image->position.top < 0)) {
y_direction *= -1;
}
}
ui_util_component_render_subcomponents(component);
}

/**
* Collects all component functions.
*/
static component_functions_t _component_functions = {
.cleanup = ui_util_component_cleanup,
.render = _render,
.on_event = ui_util_on_event_noop,
};

component_t* screensaver_create(void)
{
component_t* component = malloc(sizeof(component_t));
if (!component) {
Abort("Error: malloc screensaver");
}
memset(component, 0, sizeof(component_t));
component->f = &_component_functions;
component->dimension.width = SCREEN_WIDTH;
component->dimension.height = SCREEN_HEIGHT;
component_t* screensaver_image = image_create(
IMAGE_SCREENSAVER,
sizeof(IMAGE_SCREENSAVER),
IMAGE_SCREENSAVER_W,
IMAGE_SCREENSAVER_H,
CENTER,
component);
ui_util_add_sub_component(component, screensaver_image);
screensaver_reset(component);
return component;
}

void screensaver_reset(component_t* component)
{
component_t* image = component->sub_components.sub_components[0];
image->position.left = -image->dimension.width;
// We move the image even more out of screen, to have a moment where the screen is just black
// before the screensaver becomes visible.
image->position.left -= 30;
}
28 changes: 28 additions & 0 deletions src/ui/components/screensaver.h
@@ -0,0 +1,28 @@
// Copyright 2019 Shift Crypto AG
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef _SCREENSAVER_H_
#define _SCREENSAVER_H_

#include <ui/component.h>

component_t* screensaver_create(void);

/**
* Resets the animation so that the logo starts to scroll in from left, out of screen.
* The vertical position is unchanged, it starts where it left off.
*/
void screensaver_reset(component_t* component);

#endif
8 changes: 8 additions & 0 deletions src/ui/components/ui_images.h
Expand Up @@ -121,4 +121,12 @@ void image_lock(int x, int y, int r);
void image_unlock(int x, int y, int r);
void image_sdcard(bool mirror);

#define IMAGE_SCREENSAVER_W 17
#define IMAGE_SCREENSAVER_H 20

static const uint8_t IMAGE_SCREENSAVER[] = {
0x01, 0x40, 0x01, 0xb0, 0x01, 0xdc, 0x01, 0xef, 0x00, 0xf7, 0x80, 0x7b, 0xc0, 0x3d, 0xe0,
0x1e, 0xf0, 0x0f, 0x78, 0x0f, 0xbe, 0x1f, 0xdf, 0xdf, 0xc7, 0xff, 0xc1, 0xff, 0x84, 0x3e,
0x0f, 0x82, 0x1f, 0xf0, 0x3f, 0xfe, 0x0f, 0xfe, 0x01, 0xfc, 0x00, 0x38, 0x00};

#endif
31 changes: 30 additions & 1 deletion src/ui/event_handler.c
Expand Up @@ -15,6 +15,8 @@
#include <stddef.h>

#include "event_handler.h"
#include "screen_process.h"
#include "screen_saver.h"
#include "screen_stack.h"

static void _handle_event(component_t* component, const event_t* event)
Expand All @@ -34,5 +36,32 @@ static void _handle_event(component_t* component, const event_t* event)

void emit_event(const event_t* event)
{
_handle_event(ui_screen_stack_top(), event);
_handle_event(screen_process_get_top_component(), event);

// Reset the screensaver based on slider touch:
//
// If the current component is the screensaver itself, only reset it when releasing the touch.
// Otherwise, the screensaver would disappear, showing the previous component with the user
// still touching. This can induce anxiety, as it looks like the touch could affect trigger an
// unwanted action.
//
// If the screensaver is not active, we reset the timer with any touch interaction.
switch (event->id) {
case EVENT_TOP_SHORT_TAP:
case EVENT_BOTTOM_SHORT_TAP:
case EVENT_BOTTOM_SLIDE_RELEASED:
case EVENT_TOP_SLIDE_RELEASED:
screen_saver_reset();
break;
case EVENT_TOP_SLIDE:
case EVENT_BOTTOM_SLIDE:
case EVENT_TOP_CONTINUOUS_TAP:
case EVENT_BOTTOM_CONTINUOUS_TAP:
if (screen_saver_get() == NULL) {
screen_saver_reset();
}
break;
default:
break;
}
}
5 changes: 5 additions & 0 deletions src/ui/oled/oled.c
Expand Up @@ -305,3 +305,8 @@ void oled_off(void)
gpio_set_pin_level(PIN_OLED_ON, 0);
_enabled = false;
}

void oled_set_brightness(uint8_t value)
{
_write_cmd_with_param(OLED_CMD_SET_CONTRAST_CONTROL_FOR_BANK0, value);
}
6 changes: 6 additions & 0 deletions src/ui/oled/oled.h
Expand Up @@ -96,4 +96,10 @@ void oled_off(void);
*/
void oled_set_pixel(uint16_t x, uint16_t y, uint8_t c);

/**
* Set brightness (0x00..0xff).
* 0x00 does not mean black, just loweset brightness.
*/
void oled_set_brightness(uint8_t value);

#endif
16 changes: 10 additions & 6 deletions src/ui/screen_process.c
Expand Up @@ -18,6 +18,7 @@
#include <touch/gestures.h>
#include <ui/components/waiting.h>
#include <ui/screen_process.h>
#include <ui/screen_saver.h>
#include <ui/ugui/ugui.h>

static uint8_t screen_frame_cnt = 0;
Expand All @@ -43,12 +44,13 @@ static component_t* _get_waiting_screen(void)
return waiting_screen;
}

/*
* Select which activity we should draw next
* (or fallback to the idle screen).
*/
static component_t* _get_ui_top_component(void)
component_t* screen_process_get_top_component(void)
{
component_t* saver = screen_saver_get();
if (saver != NULL) {
return saver;
}

component_t* result = ui_screen_stack_top();
if (!result) {
return _get_waiting_screen();
Expand Down Expand Up @@ -84,7 +86,9 @@ static bool _screen_has_changed(const component_t* current_component)

void screen_process(void)
{
component_t* component = _get_ui_top_component();
screen_saver_process();

component_t* component = screen_process_get_top_component();
_screen_draw(component);

/*
Expand Down
7 changes: 7 additions & 0 deletions src/ui/screen_process.h
Expand Up @@ -20,6 +20,13 @@

void ui_screen_render_component(component_t* component);

/*
* Select which activity we should process next
* This returns the default screen if there nothing else to process.
* If the screensaver is active, this returns the screensaver.
*/
component_t* screen_process_get_top_component(void);

/**
* Runs the UI once.
*
Expand Down
74 changes: 74 additions & 0 deletions src/ui/screen_saver.c
@@ -0,0 +1,74 @@
// Copyright 2020 Shift Crypto AG
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "screen_saver.h"

#include <hardfault.h>
#include <ui/components/screensaver.h>
#ifndef TESTING
#include <ui/oled/oled.h>
#endif

#include <stdbool.h>
#include <stdint.h>

// TODO: use a TIMER interrupt to get a more accurate timer.
// 270000 is ~1min. Unit: main loop iterations.
#define ACTIVE_AFTER 270000
benma marked this conversation as resolved.
Show resolved Hide resolved

static uint32_t _counter = 0;
static bool _is_active = false;

component_t* screen_saver_get(void)
{
if (!_is_active) {
return NULL;
}
static component_t* screensaver = NULL;
if (screensaver == NULL) {
screensaver = screensaver_create();
if (screensaver == NULL) {
Abort("Could not create\nscreensaver");
}
}
return screensaver;
}

void screen_saver_process(void)
{
if (!_is_active) {
_counter++;
if (_counter > ACTIVE_AFTER) {
_is_active = true;
#ifndef TESTING
oled_set_brightness(0x00);
#endif
}
}
}

void screen_saver_reset(void)
{
if (_is_active) {
component_t* component = screen_saver_get();
if (component != NULL) {
screensaver_reset(component);
}
#ifndef TESTING
oled_set_brightness(0xFF);
#endif
_is_active = false;
}
_counter = 0;
}
38 changes: 38 additions & 0 deletions src/ui/screen_saver.h
@@ -0,0 +1,38 @@
// Copyright 2020 Shift Crypto AG
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef _UI_SCREEN_SAVER_H_
#define _UI_SCREEN_SAVER_H_

#include "component.h"

/**
* If the screensaver is active, returns a static screen saver component.
* If the screensaver is not active, returns NULL.
*/
component_t* screen_saver_get(void);

/**
* Should be called from the mainloop. Handles the timing of the screensaver.
* After a certain time, the screen saver becomes active.
* Call `screen_saver_reset()` to reset the screensaver and the timer.
*/
void screen_saver_process(void);

/**
* Call this to remove an active screensaver, or to reset the timer.
*/
void screen_saver_reset(void);

#endif