From 1a80003775ca96343cc8047cabadf899e1273b9b Mon Sep 17 00:00:00 2001
From: TheOnePerson <a.nebinger@web.de>
Date: Sun, 23 Oct 2022 13:07:32 +0200
Subject: Movement: implement auto firing of long press events and introduce
 long up event. (Also re-implement alarm_enabled and alarm_note)

---
 movement/movement.c | 44 +++++++++++++++++++++++++++++++++-----------
 movement/movement.h | 20 +++++++++++++-------
 2 files changed, 46 insertions(+), 18 deletions(-)

diff --git a/movement/movement.c b/movement/movement.c
index 09ebf0c2..c3b096e9 100644
--- a/movement/movement.c
+++ b/movement/movement.c
@@ -22,6 +22,8 @@
  * SOFTWARE.
  */
 
+#define MOVEMENT_LONG_PRESS_TICKS 64
+
 #include <stdio.h>
 #include <string.h>
 #include <limits.h>
@@ -250,11 +252,16 @@ void movement_play_signal(void) {
 }
 
 void movement_play_alarm(void) {
+    movement_play_alarm_beeps(5, BUZZER_NOTE_C8);
+}
+
+void movement_play_alarm_beeps(uint8_t rounds, BuzzerNote alarm_note) {
+    if (rounds == 0) rounds = 1;
+    if (rounds > 20) rounds = 20;
     movement_request_wake();
-    // alarm length: 75 ticks short of 5 seconds, or 4.414 seconds:
-    // our tone is 0.375 seconds of beep and 0.625 of silence, repeated five times.
-    // so 4.375 + a few ticks to wake up from sleep mode.
-    movement_state.alarm_ticks = 128 * 5 - 75;
+    movement_state.alarm_note = alarm_note;
+    // our tone is 0.375 seconds of beep and 0.625 of silence, repeated as given.
+    movement_state.alarm_ticks = 128 * rounds - 75;
     _movement_enable_fast_tick_if_needed();
 }
 
@@ -453,10 +460,13 @@ bool app_loop(void) {
     if (movement_state.alarm_ticks >= 0) {
         uint8_t buzzer_phase = (movement_state.alarm_ticks + 80) % 128;
         if(buzzer_phase == 127) {
+            // failsafe: buzzer could have been disabled in the meantime
+            if (!watch_is_buzzer_or_led_enabled()) watch_enable_buzzer();
+            // play 4 beeps plus pause
             for(uint8_t i = 0; i < 4; i++) {
                 // TODO: This method of playing the buzzer blocks the UI while it's beeping.
                 // It might be better to time it with the fast tick.
-                watch_buzzer_play_note(BUZZER_NOTE_C8, (i != 3) ? 50 : 75);
+                watch_buzzer_play_note(movement_state.alarm_note, (i != 3) ? 50 : 75);
                 if (i != 3) watch_buzzer_play_note(BUZZER_NOTE_REST, 50);
             }
         }
@@ -503,7 +513,7 @@ bool app_loop(void) {
     return can_sleep;
 }
 
-static movement_event_type_t _figure_out_button_event(bool pin_level, movement_event_type_t button_down_event_type, uint8_t *down_timestamp) {
+static movement_event_type_t _figure_out_button_event(bool pin_level, movement_event_type_t button_down_event_type, uint16_t *down_timestamp) {
     // force alarm off if the user pressed a button.
     if (movement_state.alarm_ticks) movement_state.alarm_ticks = 0;
 
@@ -513,15 +523,15 @@ static movement_event_type_t _figure_out_button_event(bool pin_level, movement_e
         *down_timestamp = movement_state.fast_ticks + 1;
         return button_down_event_type;
     } else {
-        // this line is hack but it handles the situation where the light button was held for more than 10 seconds.
+        // this line is hack but it handles the situation where the light button was held for more than 20 seconds.
         // fast tick is disabled by then, and the LED would get stuck on since there's no one left decrementing light_ticks.
         if (movement_state.light_ticks == 1) movement_state.light_ticks = 0;
         // now that that's out of the way, handle falling edge
         uint16_t diff = movement_state.fast_ticks - *down_timestamp;
         *down_timestamp = 0;
         _movement_disable_fast_tick_if_possible();
-        // any press over a half second is considered a long press.
-        if (diff > 64) return button_down_event_type + 2;
+        // any press over a half second is considered a long press. Fire the long-up event
+        if (diff > MOVEMENT_LONG_PRESS_TICKS) return button_down_event_type + 3;
         else return button_down_event_type + 1;
     }
 }
@@ -557,9 +567,21 @@ void cb_fast_tick(void) {
     movement_state.fast_ticks++;
     if (movement_state.light_ticks > 0) movement_state.light_ticks--;
     if (movement_state.alarm_ticks > 0) movement_state.alarm_ticks--;
+    // check timestamps and auto-fire the long-press events
+    // Notice: is it possible that two or more buttons have an identical timestamp? In this case
+    // only one of these buttons would receive the long press event. Don't bother for now...
+    if (movement_state.light_down_timestamp > 0)
+        if (movement_state.fast_ticks - movement_state.light_down_timestamp == MOVEMENT_LONG_PRESS_TICKS + 1) 
+            event.event_type = EVENT_LIGHT_LONG_PRESS;
+    if (movement_state.mode_down_timestamp > 0)
+        if (movement_state.fast_ticks - movement_state.mode_down_timestamp == MOVEMENT_LONG_PRESS_TICKS + 1) 
+            event.event_type = EVENT_MODE_LONG_PRESS;
+    if (movement_state.alarm_down_timestamp > 0)
+        if (movement_state.fast_ticks - movement_state.alarm_down_timestamp == MOVEMENT_LONG_PRESS_TICKS + 1) 
+            event.event_type = EVENT_ALARM_LONG_PRESS;
     // this is just a fail-safe; fast tick should be disabled as soon as the button is up, the LED times out, and/or the alarm finishes.
-    // but if for whatever reason it isn't, this forces the fast tick off after 10 seconds.
-    if (movement_state.fast_ticks >= 1280) watch_rtc_disable_periodic_callback(128);
+    // but if for whatever reason it isn't, this forces the fast tick off after 20 seconds.
+    if (movement_state.fast_ticks >= 128 * 20) watch_rtc_disable_periodic_callback(128);
 }
 
 void cb_tick(void) {
diff --git a/movement/movement.h b/movement/movement.h
index 79222e8c..6cf8cf0d 100644
--- a/movement/movement.h
+++ b/movement/movement.h
@@ -61,7 +61,8 @@ typedef union {
         // altimeter to display feet or meters as easily as it tells a thermometer to display degrees in F or C.
         bool clock_mode_24h : 1;            // indicates whether clock should use 12 or 24 hour mode.
         bool use_imperial_units : 1;        // indicates whether to use metric units (the default) or imperial.
-        uint8_t reserved : 7;               // room for more preferences if needed.
+        bool alarm_enabled : 1;             // indicates whether there is at least one alarm enabled.
+        uint8_t reserved : 6;               // room for more preferences if needed.
     } bit;
     uint32_t reg;
 } movement_settings_t;
@@ -109,13 +110,16 @@ typedef enum {
     EVENT_TIMEOUT,              // Your watch face has been inactive for a while. You may want to resign, depending on your watch face's intended use case.
     EVENT_LIGHT_BUTTON_DOWN,    // The light button has been pressed, but not yet released.
     EVENT_LIGHT_BUTTON_UP,      // The light button was pressed and released.
-    EVENT_LIGHT_LONG_PRESS,     // The light button was held for >2 seconds, and released.
+    EVENT_LIGHT_LONG_PRESS,     // The light button was held for >2 seconds, but not yet released.
+    EVENT_LIGHT_LONG_UP,        // The light button was held for >2 seconds, and released.
     EVENT_MODE_BUTTON_DOWN,     // The mode button has been pressed, but not yet released.
     EVENT_MODE_BUTTON_UP,       // The mode button was pressed and released.
-    EVENT_MODE_LONG_PRESS,      // The mode button was held for >2 seconds, and released. NOTE: your watch face will resign immediately after receiving this event.
+    EVENT_MODE_LONG_PRESS,      // The mode button was held for >2 seconds, but not yet released.
+    EVENT_MODE_LONG_UP,         // The mode button was held for >2 seconds, and released. NOTE: your watch face will resign immediately after receiving this event.
     EVENT_ALARM_BUTTON_DOWN,    // The alarm button has been pressed, but not yet released.
     EVENT_ALARM_BUTTON_UP,      // The alarm button was pressed and released.
-    EVENT_ALARM_LONG_PRESS,     // The alarm button was held for >2 seconds, and released.
+    EVENT_ALARM_LONG_PRESS,     // The alarm button was held for >2 seconds, but not yet released.
+    EVENT_ALARM_LONG_UP,        // The alarm button was held for >2 seconds, and released.
 } movement_event_type_t;
 
 typedef struct {
@@ -252,11 +256,12 @@ typedef struct {
     // alarm stuff
     int16_t alarm_ticks;
     bool is_buzzing;
+    BuzzerNote alarm_note;
 
     // button tracking for long press
-    uint8_t light_down_timestamp;
-    uint8_t mode_down_timestamp;
-    uint8_t alarm_down_timestamp;
+    uint16_t light_down_timestamp;
+    uint16_t mode_down_timestamp;
+    uint16_t alarm_down_timestamp;
 
     // background task handling
     bool needs_background_tasks_handled;
@@ -300,6 +305,7 @@ void movement_request_wake(void);
 
 void movement_play_signal(void);
 void movement_play_alarm(void);
+void movement_play_alarm_beeps(uint8_t rounds, BuzzerNote alarm_note);
 
 uint8_t movement_claim_backup_register(void);
 
-- 
cgit v1.2.3