379 lines
15 KiB
C
379 lines
15 KiB
C
#include "palette_manip.h"
|
|
#include <pebble.h>
|
|
|
|
// DEBUG memory usage layer
|
|
static TextLayer *s_memory_layer;
|
|
|
|
// declare general statics
|
|
static Window *s_main_window;
|
|
static GFont s_custom_font;
|
|
static Layer *s_time_bar_layer;
|
|
static TextLayer *s_time_layer;
|
|
static TextLayer *s_date_layer;
|
|
static BitmapLayer *s_bt_layer;
|
|
static BitmapLayer *s_guy_head_layer;
|
|
static BitmapLayer *s_guy_butt_layer;
|
|
|
|
// declare silly guy parts
|
|
static GColor8 s_random_color_current;
|
|
static GColor8 s_random_color_next;
|
|
static GBitmap *s_head_current;
|
|
static GBitmap *s_butt_current;
|
|
static GBitmap *s_head_next;
|
|
static GBitmap *s_butt_next;
|
|
static GBitmap *s_bt;
|
|
|
|
// declare positions for animation stages
|
|
static GRect s_guy_head_grect_on_screen_raised = GRect(PBL_IF_ROUND_ELSE(0, -30), PBL_IF_ROUND_ELSE(-15, -31), 260, 115);
|
|
static GRect s_guy_head_grect_off_screen_raised = GRect(PBL_IF_ROUND_ELSE(-260, -230), PBL_IF_ROUND_ELSE(-15, -31), 260, 115);
|
|
static GRect s_guy_head_grect_on_screen_resting = GRect(PBL_IF_ROUND_ELSE(0, -30), PBL_IF_ROUND_ELSE(0, -16), 260, 115);
|
|
static GRect s_guy_butt_grect_on_screen_raised = GRect(PBL_IF_ROUND_ELSE(0, -30), PBL_IF_ROUND_ELSE(160, 144), 260, 115);
|
|
static GRect s_guy_butt_grect_off_screen_raised = GRect(PBL_IF_ROUND_ELSE(260, 170), PBL_IF_ROUND_ELSE(160, 144), 260, 115);
|
|
static GRect s_guy_butt_grect_on_screen_resting = GRect(PBL_IF_ROUND_ELSE(0, -30), PBL_IF_ROUND_ELSE(145, 129), 260, 115);
|
|
|
|
// declare animations
|
|
static Animation *s_head_raise_anim;
|
|
static Animation *s_head_out_anim;
|
|
static Animation *s_head_in_anim;
|
|
static Animation *s_head_rest_anim;
|
|
static PropertyAnimation *s_head_y_prop_anim;
|
|
static PropertyAnimation *s_head_x_prop_anim;
|
|
static Animation *s_butt_raise_anim;
|
|
static Animation *s_butt_out_anim;
|
|
static Animation *s_butt_in_anim;
|
|
static Animation *s_butt_rest_anim;
|
|
static PropertyAnimation *s_butt_y_prop_anim;
|
|
static PropertyAnimation *s_butt_x_prop_anim;
|
|
|
|
// declare lookup tables
|
|
static const uint8_t s_head_count = 25;
|
|
static const uint32_t s_random_heads[] = {
|
|
RESOURCE_ID_H001, RESOURCE_ID_H002, RESOURCE_ID_H003, RESOURCE_ID_H004,
|
|
RESOURCE_ID_H005, RESOURCE_ID_H006, RESOURCE_ID_H007, RESOURCE_ID_H008,
|
|
RESOURCE_ID_H009, RESOURCE_ID_H010, RESOURCE_ID_H011, RESOURCE_ID_H012,
|
|
RESOURCE_ID_H013, RESOURCE_ID_H014, RESOURCE_ID_H015, RESOURCE_ID_H016,
|
|
RESOURCE_ID_H017, RESOURCE_ID_H018, RESOURCE_ID_H019, RESOURCE_ID_H020,
|
|
RESOURCE_ID_H021, RESOURCE_ID_H022, RESOURCE_ID_H023, RESOURCE_ID_H024,
|
|
RESOURCE_ID_H025};
|
|
static const uint8_t s_butt_count = 26;
|
|
static const uint32_t s_random_butts[] = {
|
|
RESOURCE_ID_B001, RESOURCE_ID_B002, RESOURCE_ID_B003, RESOURCE_ID_B004,
|
|
RESOURCE_ID_B005, RESOURCE_ID_B006, RESOURCE_ID_B007, RESOURCE_ID_B008,
|
|
RESOURCE_ID_B009, RESOURCE_ID_B010, RESOURCE_ID_B011, RESOURCE_ID_B012,
|
|
RESOURCE_ID_B013, RESOURCE_ID_B014, RESOURCE_ID_B015, RESOURCE_ID_B016,
|
|
RESOURCE_ID_B017, RESOURCE_ID_B018, RESOURCE_ID_B019, RESOURCE_ID_B020,
|
|
RESOURCE_ID_B021, RESOURCE_ID_B022, RESOURCE_ID_B023, RESOURCE_ID_B024,
|
|
RESOURCE_ID_B025, RESOURCE_ID_XB001};
|
|
static const uint8_t s_color_count = 20;
|
|
static const GColor8 s_dark_bg_colors[] = {
|
|
// Red
|
|
GColorFolly, GColorRed,
|
|
// Orange
|
|
GColorOrange, GColorRajah,
|
|
// Yellow
|
|
GColorIcterine, GColorPastelYellow, GColorYellow,
|
|
// Green
|
|
GColorInchworm, GColorJaegerGreen, GColorKellyGreen, GColorMintGreen,
|
|
// Blue
|
|
GColorElectricBlue, GColorTiffanyBlue, GColorVividCerulean,
|
|
// Purple
|
|
GColorLavenderIndigo, GColorPurpureus,
|
|
// Pink
|
|
GColorBrilliantRose, GColorMelon, GColorShockingPink,
|
|
// Brown
|
|
GColorRoseVale};
|
|
|
|
static void update_minute_1() {
|
|
// get a tm structure
|
|
time_t temp = time(NULL);
|
|
struct tm *tick_time = localtime(&temp);
|
|
|
|
// write the current hours and minutes into a buffer & format
|
|
static char s_time_buffer[8];
|
|
strftime(s_time_buffer, sizeof(s_time_buffer), clock_is_24h_style() ? "%H:%M" : "%I:%M", tick_time);
|
|
|
|
// write the current date into a buffer & format
|
|
static char s_date_buffer[16];
|
|
strftime(s_date_buffer, sizeof(s_date_buffer), "%m.%d", tick_time);
|
|
|
|
text_layer_set_text(s_time_layer, s_time_buffer);
|
|
text_layer_set_text(s_date_layer, s_date_buffer);
|
|
|
|
// DEBUG memory usage layer
|
|
static char s_memory_buffer[32];
|
|
snprintf(s_memory_buffer, sizeof(s_memory_buffer), "%d/%d", (int)heap_bytes_used(), (int)heap_bytes_free() + (int)heap_bytes_used());
|
|
text_layer_set_text(s_memory_layer, s_memory_buffer);
|
|
}
|
|
|
|
static void update_minute_30_out_handler(Animation *animation, bool finished, void *context) {
|
|
// destroy and reassign current
|
|
gbitmap_destroy(s_butt_current);
|
|
s_butt_current = s_butt_next;
|
|
|
|
// swap colors
|
|
replace_gbitmap_color(GColorGreen, s_random_color_next, s_butt_current, NULL);
|
|
replace_gbitmap_color(s_random_color_current, s_random_color_next, s_head_current, NULL);
|
|
s_random_color_current = s_random_color_next; // must be referenced for later replacement
|
|
|
|
// force update bitmap layers
|
|
bitmap_layer_set_bitmap(s_guy_butt_layer, s_butt_current);
|
|
bitmap_layer_set_bitmap(s_guy_head_layer, s_head_current);
|
|
|
|
// calc next values
|
|
s_butt_next = gbitmap_create_with_resource(s_random_butts[rand() % s_butt_count]);
|
|
s_random_color_next = s_dark_bg_colors[rand() % s_color_count]; // next color
|
|
}
|
|
|
|
static void update_minute_30() {
|
|
update_minute_1();
|
|
|
|
// raise
|
|
s_butt_y_prop_anim = property_animation_create_layer_frame(bitmap_layer_get_layer(s_guy_butt_layer), &s_guy_butt_grect_on_screen_resting, &s_guy_butt_grect_on_screen_raised);
|
|
s_butt_raise_anim = property_animation_get_animation(s_butt_y_prop_anim);
|
|
animation_set_curve(s_butt_raise_anim, AnimationCurveEaseOut);
|
|
animation_set_duration(s_butt_raise_anim, 350);
|
|
// out
|
|
s_butt_x_prop_anim = property_animation_create_layer_frame(bitmap_layer_get_layer(s_guy_butt_layer), &s_guy_butt_grect_on_screen_raised, &s_guy_butt_grect_off_screen_raised);
|
|
s_butt_out_anim = property_animation_get_animation(s_butt_x_prop_anim);
|
|
animation_set_curve(s_butt_out_anim, AnimationCurveEaseOut);
|
|
animation_set_duration(s_butt_out_anim, 350);
|
|
// back in
|
|
s_butt_in_anim = animation_clone(s_butt_out_anim);
|
|
animation_set_reverse(s_butt_in_anim, true);
|
|
// set handler on out (after clone)
|
|
animation_set_handlers(s_butt_out_anim, (AnimationHandlers){.stopped = update_minute_30_out_handler}, NULL);
|
|
// back to resting
|
|
s_butt_rest_anim = animation_clone(s_butt_raise_anim);
|
|
animation_set_reverse(s_butt_rest_anim, true);
|
|
|
|
// run animations
|
|
animation_schedule(animation_sequence_create(s_butt_raise_anim, s_butt_out_anim, s_butt_in_anim, s_butt_rest_anim, NULL));
|
|
|
|
// cleanup
|
|
animation_destroy(s_butt_raise_anim);
|
|
animation_destroy(s_butt_out_anim);
|
|
animation_destroy(s_butt_in_anim);
|
|
animation_destroy(s_butt_rest_anim);
|
|
property_animation_destroy(s_butt_y_prop_anim);
|
|
property_animation_destroy(s_butt_x_prop_anim);
|
|
}
|
|
|
|
static void update_minute_60_out_handler(Animation *animation, bool finished, void *context) {
|
|
// destroy and reassign current
|
|
gbitmap_destroy(s_head_current);
|
|
s_head_current = s_head_next;
|
|
|
|
// swap colors
|
|
replace_gbitmap_color(GColorGreen, s_random_color_next, s_head_current, NULL);
|
|
replace_gbitmap_color(s_random_color_current, s_random_color_next, s_butt_current, NULL);
|
|
s_random_color_current = s_random_color_next;
|
|
|
|
// force update bitmap layers
|
|
bitmap_layer_set_bitmap(s_guy_head_layer, s_head_current);
|
|
bitmap_layer_set_bitmap(s_guy_butt_layer, s_butt_current);
|
|
|
|
// calc next values
|
|
s_head_next = gbitmap_create_with_resource(s_random_heads[rand() % s_head_count]);
|
|
s_random_color_next = s_dark_bg_colors[rand() % s_color_count]; // next color
|
|
}
|
|
|
|
static void update_minute_60() {
|
|
update_minute_1();
|
|
|
|
// raise
|
|
s_head_y_prop_anim = property_animation_create_layer_frame(bitmap_layer_get_layer(s_guy_head_layer), &s_guy_head_grect_on_screen_resting, &s_guy_head_grect_on_screen_raised);
|
|
s_head_raise_anim = property_animation_get_animation(s_head_y_prop_anim);
|
|
animation_set_curve(s_head_raise_anim, AnimationCurveEaseOut);
|
|
animation_set_duration(s_head_raise_anim, 350);
|
|
// out
|
|
s_head_x_prop_anim = property_animation_create_layer_frame(bitmap_layer_get_layer(s_guy_head_layer), &s_guy_head_grect_on_screen_raised, &s_guy_head_grect_off_screen_raised);
|
|
s_head_out_anim = property_animation_get_animation(s_head_x_prop_anim);
|
|
animation_set_curve(s_head_out_anim, AnimationCurveEaseOut);
|
|
animation_set_duration(s_head_out_anim, 350);
|
|
// back in
|
|
s_head_in_anim = animation_clone(s_head_out_anim);
|
|
animation_set_reverse(s_head_in_anim, true);
|
|
// set handler on out (after clone)
|
|
animation_set_handlers(s_head_out_anim, (AnimationHandlers){.stopped = update_minute_30_out_handler}, NULL);
|
|
// back to resting
|
|
s_head_rest_anim = animation_clone(s_head_raise_anim);
|
|
animation_set_reverse(s_head_rest_anim, true);
|
|
|
|
// run animations
|
|
animation_schedule(animation_sequence_create(s_head_raise_anim, s_head_out_anim, s_head_in_anim, s_head_rest_anim, NULL));
|
|
|
|
// cleanup
|
|
animation_destroy(s_head_raise_anim);
|
|
animation_destroy(s_head_out_anim);
|
|
animation_destroy(s_head_in_anim);
|
|
animation_destroy(s_head_rest_anim);
|
|
property_animation_destroy(s_head_y_prop_anim);
|
|
property_animation_destroy(s_head_x_prop_anim);
|
|
}
|
|
|
|
static void minute_handler(struct tm *tick_time, TimeUnits units_changed) {
|
|
if (tick_time->tm_min % 60 == 0) {
|
|
update_minute_60();
|
|
} else if (tick_time->tm_min % 30 == 0) {
|
|
update_minute_30();
|
|
} else {
|
|
update_minute_1();
|
|
}
|
|
}
|
|
|
|
static void time_bar_update_proc(Layer *layer, GContext *ctx) {
|
|
graphics_context_set_fill_color(ctx, GColorWhite);
|
|
graphics_fill_rect(ctx, layer_get_bounds(layer), 0, GCornerNone);
|
|
}
|
|
|
|
// define contents of the Window upon load
|
|
static void main_window_load(Window *window) {
|
|
Layer *window_layer = window_get_root_layer(window);
|
|
|
|
// format silly guy layers
|
|
s_guy_head_layer = bitmap_layer_create(s_guy_head_grect_on_screen_resting);
|
|
s_guy_butt_layer = bitmap_layer_create(s_guy_butt_grect_on_screen_resting);
|
|
bitmap_layer_set_compositing_mode(s_guy_head_layer, GCompOpSet);
|
|
bitmap_layer_set_alignment(s_guy_head_layer, GAlignLeft);
|
|
bitmap_layer_set_bitmap(s_guy_head_layer, s_head_current);
|
|
bitmap_layer_set_compositing_mode(s_guy_butt_layer, GCompOpSet);
|
|
bitmap_layer_set_alignment(s_guy_butt_layer, GAlignTopLeft);
|
|
bitmap_layer_set_bitmap(s_guy_butt_layer, s_butt_current);
|
|
|
|
// format time layer
|
|
s_time_layer = text_layer_create(GRect(0, PBL_IF_ROUND_ELSE(109, 93), PBL_IF_ROUND_ELSE(260, 200), 34));
|
|
text_layer_set_background_color(s_time_layer, GColorClear);
|
|
text_layer_set_font(s_time_layer, s_custom_font);
|
|
text_layer_set_text_alignment(s_time_layer, GTextAlignmentCenter);
|
|
|
|
// format date layer
|
|
s_date_layer = text_layer_create(GRect(0, PBL_IF_ROUND_ELSE(121, 105), PBL_IF_ROUND_ELSE(69, 39), 14));
|
|
text_layer_set_background_color(s_date_layer, GColorClear);
|
|
text_layer_set_font(s_date_layer, fonts_get_system_font(FONT_KEY_GOTHIC_14_BOLD));
|
|
text_layer_set_text_alignment(s_date_layer, GTextAlignmentCenter);
|
|
|
|
// format BT indicator layer
|
|
s_bt_layer = bitmap_layer_create(GRect(PBL_IF_ROUND_ELSE(191, 161), PBL_IF_ROUND_ELSE(118, 102), PBL_IF_ROUND_ELSE(69, 39), 24));
|
|
bitmap_layer_set_compositing_mode(s_bt_layer, GCompOpSet);
|
|
bitmap_layer_set_alignment(s_bt_layer, GAlignCenter);
|
|
bitmap_layer_set_bitmap(s_bt_layer, s_bt);
|
|
|
|
// create time bar layer
|
|
s_time_bar_layer = layer_create(GRect(0, PBL_IF_ROUND_ELSE(115, 99), PBL_IF_ROUND_ELSE(260, 200), 30));
|
|
layer_set_update_proc(s_time_bar_layer, time_bar_update_proc);
|
|
text_layer_set_text_alignment(s_date_layer, GTextAlignmentCenter);
|
|
|
|
// DEBUG memory usage layer
|
|
s_memory_layer = text_layer_create(GRect(0, PBL_IF_ROUND_ELSE(220, 190), PBL_IF_ROUND_ELSE(260, 200), 14));
|
|
text_layer_set_background_color(s_memory_layer, GColorClear);
|
|
text_layer_set_font(s_memory_layer, fonts_get_system_font(FONT_KEY_GOTHIC_14));
|
|
text_layer_set_text_color(s_memory_layer, GColorWhite);
|
|
PBL_IF_ROUND_ELSE(text_layer_set_text_alignment(s_memory_layer, GTextAlignmentCenter), text_layer_set_text_alignment(s_memory_layer, GTextAlignmentLeft));
|
|
|
|
// add layers as children to window
|
|
layer_add_child(window_layer, bitmap_layer_get_layer(s_guy_head_layer));
|
|
layer_add_child(window_layer, bitmap_layer_get_layer(s_guy_butt_layer));
|
|
layer_add_child(window_layer, s_time_bar_layer);
|
|
layer_add_child(window_layer, text_layer_get_layer(s_time_layer));
|
|
layer_add_child(window_layer, text_layer_get_layer(s_date_layer));
|
|
layer_add_child(window_layer, bitmap_layer_get_layer(s_bt_layer));
|
|
|
|
// DEBUG memory usage layer
|
|
layer_add_child(window_layer, text_layer_get_layer(s_memory_layer));
|
|
}
|
|
|
|
// free memory on Window close
|
|
static void main_window_unload(Window *window) {
|
|
// destroy layers
|
|
bitmap_layer_destroy(s_guy_head_layer);
|
|
bitmap_layer_destroy(s_guy_butt_layer);
|
|
text_layer_destroy(s_time_layer);
|
|
text_layer_destroy(s_date_layer);
|
|
layer_destroy(s_time_bar_layer);
|
|
|
|
// DEBUG memory usage layer
|
|
text_layer_destroy(s_memory_layer);
|
|
}
|
|
|
|
static void up_single_click_handler(ClickRecognizerRef recognizer, void *context) {
|
|
Window *window = (Window *)context;
|
|
update_minute_60();
|
|
}
|
|
|
|
static void down_single_click_handler(ClickRecognizerRef recognizer, void *context) {
|
|
Window *window = (Window *)context;
|
|
update_minute_30();
|
|
}
|
|
|
|
static void click_config_provider(Window *window) {
|
|
window_single_click_subscribe(BUTTON_ID_UP, up_single_click_handler);
|
|
window_single_click_subscribe(BUTTON_ID_DOWN, down_single_click_handler);
|
|
}
|
|
|
|
// set up the app on launch (don't put app logic in here);
|
|
static void init() {
|
|
// create main Window element and assign to pointer
|
|
s_main_window = window_create();
|
|
|
|
// set handlers to manage the elements inside the Window
|
|
window_set_window_handlers(s_main_window, (WindowHandlers){
|
|
.load = main_window_load,
|
|
.unload = main_window_unload});
|
|
|
|
// set app background color
|
|
window_set_background_color(s_main_window, GColorBlack);
|
|
|
|
// load initial batch of silly guy parts into memory
|
|
s_head_current = gbitmap_create_with_resource(s_random_heads[rand() % s_head_count]);
|
|
s_butt_current = gbitmap_create_with_resource(s_random_butts[rand() % s_butt_count]);
|
|
s_head_next = gbitmap_create_with_resource(s_random_heads[rand() % s_head_count]);
|
|
s_butt_next = gbitmap_create_with_resource(s_random_butts[rand() % s_butt_count]);
|
|
|
|
// pick starting colors
|
|
s_random_color_current = s_dark_bg_colors[rand() % s_color_count];
|
|
replace_gbitmap_color(GColorGreen, s_random_color_current, s_head_current, NULL);
|
|
replace_gbitmap_color(GColorGreen, s_random_color_current, s_butt_current, NULL);
|
|
// don't overwrite current color, as it will need to be replaced
|
|
s_random_color_next = s_dark_bg_colors[rand() % s_color_count];
|
|
|
|
// load custom font
|
|
s_custom_font = fonts_load_custom_font(resource_get_handle(RESOURCE_ID_FONT_RETRO_COMPUTER_34));
|
|
|
|
// load bluetooth indicator
|
|
s_bt = gbitmap_create_with_resource(RESOURCE_ID_BT);
|
|
|
|
// show the Window on the watch, with animated=true
|
|
window_stack_push(s_main_window, true);
|
|
|
|
// make sure the time is displayed from the start
|
|
update_minute_1();
|
|
|
|
// register with TickTimerService
|
|
tick_timer_service_subscribe(MINUTE_UNIT, minute_handler);
|
|
|
|
// register with ClickConfigProvider
|
|
window_set_click_config_provider(s_main_window, (ClickConfigProvider)click_config_provider);
|
|
}
|
|
|
|
// free memory on app exit
|
|
static void deinit() {
|
|
// unload font
|
|
fonts_unload_custom_font(s_custom_font);
|
|
|
|
// destroy gbitmaps
|
|
gbitmap_destroy(s_head_current);
|
|
gbitmap_destroy(s_butt_current);
|
|
gbitmap_destroy(s_head_next);
|
|
gbitmap_destroy(s_butt_next);
|
|
gbitmap_destroy(s_bt);
|
|
|
|
// destroy window
|
|
window_destroy(s_main_window);
|
|
}
|
|
|
|
int main(void) {
|
|
init();
|
|
app_event_loop();
|
|
deinit();
|
|
}
|