diff --git a/legacy/watch_faces/complication/wake_face.c b/legacy/watch_faces/complication/wake_face.c deleted file mode 100644 index 30982da0..00000000 --- a/legacy/watch_faces/complication/wake_face.c +++ /dev/null @@ -1,144 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2022 Josh Berson, building on Wesley Ellis’ countdown_face.c - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include -#include -#include "wake_face.h" -#include "watch.h" -#include "watch_utility.h" - -// -// Private -// - -static -void _wake_face_update_display(wake_face_state_t *state) { - uint8_t hour = state->hour; - - watch_clear_display(); - if ( movement_clock_mode_24h() ) - watch_set_indicator(WATCH_INDICATOR_24H); - else { - if ( hour >= 12 ) - watch_set_indicator(WATCH_INDICATOR_PM); - hour = hour % 12 ? hour % 12 : 12; - } - - if ( state->mode ) - watch_set_indicator(WATCH_INDICATOR_BELL); - - static char lcdbuf[11]; - sprintf(lcdbuf, "WA %2d%02d ", hour, state->minute); - - watch_set_colon(); - watch_display_string(lcdbuf, 0); -} - -// -// Exported -// - -void wake_face_setup(uint8_t watch_face_index, void **context_ptr) { - (void) watch_face_index; - - if (*context_ptr == NULL) { - *context_ptr = malloc(sizeof(wake_face_state_t)); - wake_face_state_t *state = (wake_face_state_t *)*context_ptr; - memset(*context_ptr, 0, sizeof(wake_face_state_t)); - - state->hour = 5; - state->minute = 0; - state->mode = 0; - } -} - -void wake_face_activate(void *context) { - (void) context; -} -void wake_face_resign(void *context) { - (void) context; -} - -movement_watch_face_advisory_t wake_face_advise(void *context) { - wake_face_state_t *state = (wake_face_state_t *)context; - movement_watch_face_advisory_t retval = { 0 }; - - if ( state->mode ) { - watch_date_time_t now = watch_rtc_get_date_time(); - retval.wants_background_task = state->hour==now.unit.hour && state->minute==now.unit.minute; - // We’re at the mercy of the advise handler - // In Safari, the emulator triggers at the ›end‹ of the minute - // Converting to Unix timestamps and taking a difference between now and wake - // is not an easy win — because the timestamp for wake has to rely on now - // for its date. So first we’d have to see if the TOD of wake is after that - // of now. If it is, take tomorrow’s date, calculating month and year rollover - // if need be. - } - - return retval; -} - -bool wake_face_loop(movement_event_t event, void *context) { - wake_face_state_t *state = (wake_face_state_t *)context; - - switch (event.event_type) { - case EVENT_ACTIVATE: - case EVENT_TICK: - _wake_face_update_display(state); - break; - case EVENT_LIGHT_BUTTON_UP: - state->hour = (state->hour + 1) % 24; - _wake_face_update_display(state); - break; - case EVENT_LIGHT_LONG_PRESS: - state->hour = (state->hour + 6) % 24; - _wake_face_update_display(state); - break; - case EVENT_ALARM_BUTTON_UP: - state->minute = (state->minute + 10) % 60; - _wake_face_update_display(state); - break; - case EVENT_ALARM_LONG_PRESS: - state->mode ^= 1; - _wake_face_update_display(state); - break; - case EVENT_BACKGROUND_TASK: - movement_play_alarm(); - // 2022-07-23: Thx @joeycastillo for the dedicated “alarm” signal - break; - case EVENT_TIMEOUT: - movement_move_to_face(0); - break; - case EVENT_LOW_ENERGY_UPDATE: - break; - case EVENT_LIGHT_BUTTON_DOWN: - // don't light up every time light is hit - break; - default: - movement_default_loop_handler(event); - break; - } - - return true; -} diff --git a/movement_faces.h b/movement_faces.h index 7baa7145..0ac1545e 100644 --- a/movement_faces.h +++ b/movement_faces.h @@ -27,6 +27,7 @@ #include "clock_face.h" #include "beats_face.h" #include "world_clock_face.h" +#include "alarm_face.h" #include "advanced_alarm_face.h" #include "countdown_face.h" #include "stopwatch_face.h" diff --git a/watch-faces.mk b/watch-faces.mk index 610c51ac..8a9d29c8 100644 --- a/watch-faces.mk +++ b/watch-faces.mk @@ -2,6 +2,7 @@ SRCS += \ ./watch-faces/clock/clock_face.c \ ./watch-faces/clock/beats_face.c \ ./watch-faces/clock/world_clock_face.c \ + ./watch-faces/complication/alarm_face.c \ ./watch-faces/complication/advanced_alarm_face.c \ ./watch-faces/complication/countdown_face.c \ ./watch-faces/complication/stopwatch_face.c \ diff --git a/watch-faces/complication/alarm_face.c b/watch-faces/complication/alarm_face.c new file mode 100644 index 00000000..010a8a41 --- /dev/null +++ b/watch-faces/complication/alarm_face.c @@ -0,0 +1,193 @@ +/* + * MIT License + * + * Copyright (c) 2022 Josh Berson, building on Wesley Ellis’ countdown_face.c + * Copyright (c) 2025 Joey Castillo + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include "alarm_face.h" +#include "watch.h" +#include "watch_utility.h" + +// +// Private +// + +static void _alarm_face_display_alarm_time(alarm_face_state_t *state) { + uint8_t hour = state->hour; + + if ( movement_clock_mode_24h() ) + watch_set_indicator(WATCH_INDICATOR_24H); + else { + if ( hour >= 12 ) watch_set_indicator(WATCH_INDICATOR_PM); + else watch_clear_indicator(WATCH_INDICATOR_PM); + hour = hour % 12 ? hour % 12 : 12; + } + + static char lcdbuf[7]; + sprintf(lcdbuf, "%2d%02d ", hour, state->minute); + + watch_display_text(WATCH_POSITION_BOTTOM, lcdbuf); +} + +static inline void button_beep() { + // play a beep as confirmation for a button press (if applicable) + if (movement_button_should_sound()) watch_buzzer_play_note_with_volume(BUZZER_NOTE_C7, 50, movement_button_volume()); +} + +// +// Exported +// + +void alarm_face_setup(uint8_t watch_face_index, void **context_ptr) { + (void) watch_face_index; + + if (*context_ptr == NULL) { + *context_ptr = malloc(sizeof(alarm_face_state_t)); + alarm_face_state_t *state = (alarm_face_state_t *)*context_ptr; + memset(*context_ptr, 0, sizeof(alarm_face_state_t)); + + // default to an 8:00 AM alarm time. + state->hour = 8; + } +} + +void alarm_face_activate(void *context) { + alarm_face_state_t *state = (alarm_face_state_t *)context; + state->setting_mode = ALARM_FACE_SETTING_MODE_NONE; +} +void alarm_face_resign(void *context) { + (void) context; +} + +bool alarm_face_loop(movement_event_t event, void *context) { + alarm_face_state_t *state = (alarm_face_state_t *)context; + + switch (event.event_type) { + case EVENT_ACTIVATE: + watch_display_text_with_fallback(WATCH_POSITION_TOP_LEFT, "ALM", "AL"); + if (state->alarm_is_on) watch_set_indicator(WATCH_INDICATOR_SIGNAL); + watch_set_colon(); + _alarm_face_display_alarm_time(state); + break; + case EVENT_TICK: + // No action needed for tick events in normal mode; we displayed our stuff in EVENT_ACTIVATE. + if (state->setting_mode == ALARM_FACE_SETTING_MODE_NONE) + break; + + // but in settings mode, we need to blink up the parameter we're setting. + _alarm_face_display_alarm_time(state); + if (event.subsecond % 2 == 0) + watch_display_text((state->setting_mode == ALARM_FACE_SETTING_MODE_SETTING_HOUR) ? WATCH_POSITION_HOURS : WATCH_POSITION_MINUTES, " "); + break; + case EVENT_LIGHT_BUTTON_DOWN: + switch (state->setting_mode) { + case ALARM_FACE_SETTING_MODE_NONE: + // If we're not in a setting mode, turn on the LED like normal. + movement_illuminate_led(); + break; + case ALARM_FACE_SETTING_MODE_SETTING_HOUR: + // If we're setting the hour, advance to minute set mode. + state->setting_mode = ALARM_FACE_SETTING_MODE_SETTING_MINUTE; + break; + case ALARM_FACE_SETTING_MODE_SETTING_MINUTE: + // If we're setting the minute, advance back to normal mode and cancel fast tick. + state->setting_mode = ALARM_FACE_SETTING_MODE_NONE; + movement_request_tick_frequency(1); + // beep to confirm setting. + button_beep(); + // also turn the alarm on since they just set it. + state->alarm_is_on = 1; + watch_set_indicator(WATCH_INDICATOR_SIGNAL); + _alarm_face_display_alarm_time(state); + break; + } + break; + case EVENT_ALARM_BUTTON_UP: + if (state->setting_mode == ALARM_FACE_SETTING_MODE_NONE) { + // in normal mode, toggle alarm on/off. + state->alarm_is_on ^= 1; + if ( state->alarm_is_on ) watch_set_indicator(WATCH_INDICATOR_SIGNAL); + else watch_clear_indicator(WATCH_INDICATOR_SIGNAL); + } + break; + case EVENT_ALARM_BUTTON_DOWN: + switch (state->setting_mode) { + case ALARM_FACE_SETTING_MODE_NONE: + // nothing to do here, alarm toggle is handled in EVENT_ALARM_BUTTON_UP. + break; + case ALARM_FACE_SETTING_MODE_SETTING_HOUR: + // increment hour, wrap around to 0 at 23. + state->hour = (state->hour + 1) % 24; + break; + case ALARM_FACE_SETTING_MODE_SETTING_MINUTE: + // increment minute, wrap around to 0 at 59. + state->minute = (state->minute + 1) % 60; + break; + } + _alarm_face_display_alarm_time(state); + break; + case EVENT_ALARM_LONG_PRESS: + if (state->setting_mode == ALARM_FACE_SETTING_MODE_NONE) { + // long press in normal mode: move to hour setting mode, request fast tick. + state->setting_mode = ALARM_FACE_SETTING_MODE_SETTING_HOUR; + movement_request_tick_frequency(4); + button_beep(); + } + break; + case EVENT_BACKGROUND_TASK: + movement_play_alarm(); + // 2022-07-23: Thx @joeycastillo for the dedicated “alarm” signal + break; + case EVENT_TIMEOUT: + movement_move_to_face(0); + break; + case EVENT_LOW_ENERGY_UPDATE: + break; + default: + movement_default_loop_handler(event); + break; + } + + return true; +} + +movement_watch_face_advisory_t alarm_face_advise(void *context) { + alarm_face_state_t *state = (alarm_face_state_t *)context; + movement_watch_face_advisory_t retval = { 0 }; + + if ( state->alarm_is_on ) { + retval.has_active_alarm = true; + watch_date_time_t now = movement_get_local_date_time(); + retval.wants_background_task = (state->hour==now.unit.hour && state->minute==now.unit.minute); + // We’re at the mercy of the advise handler + // In Safari, the emulator triggers at the ›end‹ of the minute + // Converting to Unix timestamps and taking a difference between now and wake + // is not an easy win — because the timestamp for wake has to rely on now + // for its date. So first we’d have to see if the TOD of wake is after that + // of now. If it is, take tomorrow’s date, calculating month and year rollover + // if need be. + } + + return retval; +} diff --git a/legacy/watch_faces/complication/wake_face.h b/watch-faces/complication/alarm_face.h similarity index 67% rename from legacy/watch_faces/complication/wake_face.h rename to watch-faces/complication/alarm_face.h index 008a5403..01189438 100644 --- a/legacy/watch_faces/complication/wake_face.h +++ b/watch-faces/complication/alarm_face.h @@ -2,6 +2,7 @@ * MIT License * * Copyright (c) 2022 Josh Berson + * Copyright (c) 2025 Joey Castillo * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -22,11 +23,10 @@ * SOFTWARE. */ -#ifndef WAKE_FACE_H_ -#define WAKE_FACE_H_ +#pragma once /* - * WAKE daily alarm face + * Daily ALARM face (formerly WAKE Face) * * Basic daily alarm clock face. Seems useful if nothing else in the interest * of feature parity with the F-91W’s OEM module, 593. @@ -42,25 +42,30 @@ #include "movement.h" +// enum for setting alarm time +typedef enum { + ALARM_FACE_SETTING_MODE_NONE = 0, + ALARM_FACE_SETTING_MODE_SETTING_HOUR, + ALARM_FACE_SETTING_MODE_SETTING_MINUTE +} alarm_face_setting_mode_t; + typedef struct { uint32_t hour : 5; uint32_t minute : 6; - uint32_t mode : 1; -} wake_face_state_t; + uint32_t alarm_is_on : 1; + alarm_face_setting_mode_t setting_mode : 2; +} alarm_face_state_t; -void wake_face_setup(uint8_t watch_face_index, void **context_ptr); -void wake_face_activate(void *context); -bool wake_face_loop(movement_event_t event, void *context); -void wake_face_resign(void *context); -movement_watch_face_advisory_t wake_face_advise(void *context); +void alarm_face_setup(uint8_t watch_face_index, void **context_ptr); +void alarm_face_activate(void *context); +bool alarm_face_loop(movement_event_t event, void *context); +void alarm_face_resign(void *context); +movement_watch_face_advisory_t alarm_face_advise(void *context); -#define wake_face ((const watch_face_t){ \ - wake_face_setup, \ - wake_face_activate, \ - wake_face_loop, \ - wake_face_resign, \ - wake_face_advise \ +#define alarm_face ((const watch_face_t){ \ + alarm_face_setup, \ + alarm_face_activate, \ + alarm_face_loop, \ + alarm_face_resign, \ + alarm_face_advise \ }) - -#endif // WAKE_FACE_H_ -