Files
Trigalarm/src/c/main.c
T

269 lines
8.7 KiB
C

#include "src/resource_ids.auto.h"
#include <pebble.h>
// types
typedef struct SetterTime {
uint8_t HourWIP;
uint8_t MinuteWIP;
uint8_t HourSet;
uint8_t MinuteSet;
} __attribute__((__packed__)) SetterTime;
// layer-age
static Window *s_window;
static Layer *s_top_bar_layer;
static Layer *s_bottom_bar_layer;
static BitmapLayer *s_clock_bmp_layer;
static BitmapLayer *s_check_bmp_layer;
static TextLayer *s_countdown_txt_layer;
static TextLayer *s_setter_txt_layer;
static Layer *s_highlight_layer;
// bitmaps
static GBitmap *s_clock_bmp;
static GBitmap *s_check_bmp;
// global vars
static uint8_t s_highlight_pos = 0;
static struct SetterTime s_setter_time;
// constants
static const uint8_t s_edge_bar_height = ((PBL_DISPLAY_HEIGHT / 7) * 2);
static const GColor s_primary_color = GColorJaegerGreen;
static const GColor s_highlight_color = GColorMediumAquamarine;
/////////////////////////////////
static void seconds_handler(struct tm *tick_time, TimeUnits units_changed) {
struct tm now_tm = *tick_time;
// Build the target time today
struct tm target_tm = now_tm;
target_tm.tm_hour = s_setter_time.HourSet;
target_tm.tm_min = s_setter_time.MinuteSet;
target_tm.tm_sec = 0;
time_t now = mktime(&now_tm);
time_t target = mktime(&target_tm);
if (target <= now) {
target += SECONDS_PER_DAY;
}
int32_t diff = (int32_t)(target - now);
uint8_t hours = diff / 3600;
uint8_t minutes = (diff % 3600) / 60;
uint8_t seconds = diff % 60;
static char s_countdown_buffer[9];
snprintf(s_countdown_buffer, sizeof(s_countdown_buffer),
"%02d:%02d:%02d", hours, minutes, seconds);
text_layer_set_text(s_countdown_txt_layer, s_countdown_buffer);
}
static void update_countdown_display() {
time_t now = time(NULL);
seconds_handler(localtime(&now), SECOND_UNIT);
}
static void update_setter_display() {
static char s_setter_buffer[PBL_IF_ROUND_ELSE(8, 12)];
snprintf(s_setter_buffer, sizeof(s_setter_buffer), PBL_IF_ROUND_ELSE("%02d : %02d", "%02d : %02d"), s_setter_time.HourWIP, s_setter_time.MinuteWIP);
text_layer_set_text(s_setter_txt_layer, s_setter_buffer);
}
static void up_click_handler(void *recognizer, void *ctx) {
switch (s_highlight_pos) {
case 0:
if (s_setter_time.HourWIP == 23) {
s_setter_time.HourWIP = 0;
} else {
s_setter_time.HourWIP++;
}
break;
case 1:
if (s_setter_time.MinuteWIP == 59) {
s_setter_time.MinuteWIP = 0;
} else {
s_setter_time.MinuteWIP++;
}
break;
default:
return;
}
update_setter_display();
}
static void down_click_handler(void *recognizer, void *ctx) {
switch (s_highlight_pos) {
case 0:
if (s_setter_time.HourWIP == 0) {
s_setter_time.HourWIP = 23;
} else {
s_setter_time.HourWIP--;
}
break;
case 1:
if (s_setter_time.MinuteWIP == 0) {
s_setter_time.MinuteWIP = 59;
} else {
s_setter_time.MinuteWIP--;
}
break;
default:
return;
}
update_setter_display();
}
static void select_click_handler(void *recognizer, void *ctx) {
if (s_highlight_pos == 2) {
s_setter_time.HourSet = s_setter_time.HourWIP;
s_setter_time.MinuteSet = s_setter_time.MinuteWIP;
update_countdown_display();
s_highlight_pos = 0;
} else {
s_highlight_pos++;
}
layer_mark_dirty(s_highlight_layer);
layer_mark_dirty(s_bottom_bar_layer);
}
static void back_click_handler(void *recognizer, void *ctx) {
if (s_highlight_pos == 0) {
window_stack_pop_all(true); // exit app
} else {
s_highlight_pos--;
}
layer_mark_dirty(s_highlight_layer);
layer_mark_dirty(s_bottom_bar_layer);
}
static void click_config_provider(void *ctx) {
window_single_repeating_click_subscribe(BUTTON_ID_UP, 60, up_click_handler);
window_single_repeating_click_subscribe(BUTTON_ID_DOWN, 60, down_click_handler);
window_single_click_subscribe(BUTTON_ID_SELECT, select_click_handler);
window_single_click_subscribe(BUTTON_ID_BACK, back_click_handler);
}
static void top_bar_layer_update_proc(Layer *layer, GContext *ctx) {
graphics_context_set_fill_color(ctx, s_primary_color);
graphics_fill_rect(ctx, layer_get_bounds(layer), 0, GCornerNone);
}
static void bottom_bar_layer_update_proc(Layer *layer, GContext *ctx) {
graphics_context_set_fill_color(ctx, (s_highlight_pos == 2 ? s_highlight_color : s_primary_color));
graphics_fill_rect(ctx, layer_get_bounds(layer), 0, GCornerNone);
}
static void highlight_layer_update_proc(Layer *layer, GContext *ctx) {
GRect fill_area = layer_get_bounds(layer);
fill_area.size.w = PBL_DISPLAY_WIDTH / 2;
switch (s_highlight_pos) {
case 2:
return; // do not draw highlight
case 1:
// draw highlight on minutes site
fill_area.origin.x = fill_area.size.w;
break;
}
graphics_context_set_fill_color(ctx, s_highlight_color);
graphics_fill_rect(ctx, fill_area, 0, GCornerNone);
}
static void setter_window_load() {
s_setter_time.HourWIP = 0;
s_setter_time.MinuteWIP = 0;
s_setter_time.HourSet = 0;
s_setter_time.MinuteSet = 0;
Layer *window_layer = window_get_root_layer(s_window);
// top&bottom bars
static const GRect top_bar_grect = GRect(0, 0, PBL_DISPLAY_WIDTH, s_edge_bar_height);
s_top_bar_layer = layer_create(top_bar_grect);
layer_set_update_proc(s_top_bar_layer, top_bar_layer_update_proc);
layer_add_child(window_layer, s_top_bar_layer);
static const GRect bottom_bar_grect = GRect(0, PBL_DISPLAY_HEIGHT - s_edge_bar_height, PBL_DISPLAY_WIDTH, s_edge_bar_height);
s_bottom_bar_layer = layer_create(bottom_bar_grect);
layer_set_update_proc(s_bottom_bar_layer, bottom_bar_layer_update_proc);
layer_add_child(window_layer, s_bottom_bar_layer);
// clock icon
s_clock_bmp = gbitmap_create_with_resource(RESOURCE_ID_CLOCK35);
s_clock_bmp_layer = bitmap_layer_create(GRect(PBL_IF_ROUND_ELSE(0, 8), PBL_IF_ROUND_ELSE(6, 0), top_bar_grect.size.w, top_bar_grect.size.h));
bitmap_layer_set_bitmap(s_clock_bmp_layer, s_clock_bmp);
bitmap_layer_set_alignment(s_clock_bmp_layer, PBL_IF_ROUND_ELSE(GAlignTop, GAlignLeft));
bitmap_layer_set_compositing_mode(s_clock_bmp_layer, GCompOpSet);
layer_add_child(window_layer, bitmap_layer_get_layer(s_clock_bmp_layer));
// check icon
s_check_bmp = gbitmap_create_with_resource(RESOURCE_ID_CHECK);
s_check_bmp_layer = bitmap_layer_create(bottom_bar_grect);
bitmap_layer_set_bitmap(s_check_bmp_layer, s_check_bmp);
bitmap_layer_set_compositing_mode(s_check_bmp_layer, GCompOpSet);
layer_add_child(window_layer, bitmap_layer_get_layer(s_check_bmp_layer));
// countdown
s_countdown_txt_layer = text_layer_create(GRect(PBL_IF_ROUND_ELSE(0, 39), (top_bar_grect.size.h / 2) + PBL_IF_ROUND_ELSE(10, -10), PBL_IF_ROUND_ELSE(top_bar_grect.size.w, top_bar_grect.size.w - 39), 32));
text_layer_set_text_alignment(s_countdown_txt_layer, GTextAlignmentCenter);
text_layer_set_text_color(s_countdown_txt_layer, GColorWhite);
text_layer_set_background_color(s_countdown_txt_layer, GColorClear);
text_layer_set_font(s_countdown_txt_layer, fonts_load_custom_font(resource_get_handle(RESOURCE_ID_FONT_LECOISH_MONO_BOLD_20)));
layer_add_child(window_layer, text_layer_get_layer(s_countdown_txt_layer));
update_countdown_display();
// highlighter
s_highlight_layer = layer_create(GRect(0, top_bar_grect.size.h, PBL_DISPLAY_WIDTH, PBL_DISPLAY_HEIGHT - (2 * top_bar_grect.size.h)));
layer_set_update_proc(s_highlight_layer, highlight_layer_update_proc);
layer_add_child(window_layer, s_highlight_layer);
// setter
s_setter_txt_layer = text_layer_create(GRect(0, (PBL_DISPLAY_HEIGHT / 2) - PBL_IF_ROUND_ELSE(30, 20), PBL_DISPLAY_WIDTH, PBL_IF_ROUND_ELSE(60, 40)));
text_layer_set_text_alignment(s_setter_txt_layer, GTextAlignmentCenter);
text_layer_set_background_color(s_setter_txt_layer, GColorClear);
text_layer_set_font(s_setter_txt_layer, fonts_load_custom_font(resource_get_handle(PBL_IF_ROUND_ELSE(RESOURCE_ID_FONT_LECOISH_MONO_BOLD_60, RESOURCE_ID_FONT_LECOISH_MONO_BOLD_40))));
layer_add_child(window_layer, text_layer_get_layer(s_setter_txt_layer));
update_setter_display();
tick_timer_service_subscribe(SECOND_UNIT, seconds_handler);
}
static void setter_window_unload() {
layer_destroy(s_highlight_layer);
text_layer_destroy(s_setter_txt_layer);
text_layer_destroy(s_countdown_txt_layer);
bitmap_layer_destroy(s_check_bmp_layer);
bitmap_layer_destroy(s_clock_bmp_layer);
gbitmap_destroy(s_check_bmp);
gbitmap_destroy(s_clock_bmp);
layer_destroy(s_bottom_bar_layer);
layer_destroy(s_top_bar_layer);
}
static void init() {
s_window = window_create();
switch (launch_reason()) {
case APP_LAUNCH_WAKEUP:
break; // TODO math time!
default:
window_set_window_handlers(
s_window,
(WindowHandlers){.load = setter_window_load, .unload = setter_window_unload});
window_set_click_config_provider(s_window, click_config_provider);
window_stack_push(s_window, false);
};
}
static void deinit() {
window_destroy(s_window);
}
int main(void) {
init();
app_event_loop();
deinit();
}