+++ /dev/null
-/*
- * 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 <stdlib.h>
-#include <string.h>
-#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;
-}
+++ /dev/null
-/*
- * MIT License
- *
- * Copyright (c) 2022 Josh Berson
- *
- * 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.
- */
-
-#ifndef WAKE_FACE_H_
-#define WAKE_FACE_H_
-
-/*
- * WAKE daily alarm 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.
- *
- * Also experiments with caret-free UI: One button cycles hours, the other
- * minutes, so there’s no toggling between display and adjust modes and no
- * cycling the caret through the UI.
- * º LIGHT advances hour by 1
- * º LIGHT long press advances hour by 6
- * º ALARM advances minute by 10
- * º ALARM long press cycles through signal modes (just one at the moment)
- */
-
-#include "movement.h"
-
-typedef struct {
- uint32_t hour : 5;
- uint32_t minute : 6;
- uint32_t mode : 1;
-} wake_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);
-
-#define wake_face ((const watch_face_t){ \
- wake_face_setup, \
- wake_face_activate, \
- wake_face_loop, \
- wake_face_resign, \
- wake_face_advise \
-})
-
-#endif // WAKE_FACE_H_
-
#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"
./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 \
--- /dev/null
+/*
+ * 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 <stdlib.h>
+#include <string.h>
+#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;
+}
--- /dev/null
+/*
+ * 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
+ * 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.
+ */
+
+#pragma once
+
+/*
+ * 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.
+ *
+ * Also experiments with caret-free UI: One button cycles hours, the other
+ * minutes, so there’s no toggling between display and adjust modes and no
+ * cycling the caret through the UI.
+ * º LIGHT advances hour by 1
+ * º LIGHT long press advances hour by 6
+ * º ALARM advances minute by 10
+ * º ALARM long press cycles through signal modes (just one at the moment)
+ */
+
+#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 alarm_is_on : 1;
+ alarm_face_setting_mode_t setting_mode : 2;
+} alarm_face_state_t;
+
+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 alarm_face ((const watch_face_t){ \
+ alarm_face_setup, \
+ alarm_face_activate, \
+ alarm_face_loop, \
+ alarm_face_resign, \
+ alarm_face_advise \
+})