aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorReiner Herrmann <reiner@reiner-h.de>2026-01-10 18:45:29 +0100
committerReiner Herrmann <reiner@reiner-h.de>2026-01-10 18:46:14 +0100
commit0aac353f636126a809a4b3c521d64e576682019c (patch)
tree277bbecbb6a2c4e1ba0d0e967e75d3cf61b54833 /src
initial commit
Diffstat (limited to 'src')
-rw-r--r--src/c/watchface.c289
1 files changed, 289 insertions, 0 deletions
diff --git a/src/c/watchface.c b/src/c/watchface.c
new file mode 100644
index 0000000..0c12d70
--- /dev/null
+++ b/src/c/watchface.c
@@ -0,0 +1,289 @@
+/*
+ * Copyright (C) 2025 Reiner Herrmann
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#include <pebble.h>
+
+static Window *main_window;
+static TextLayer *clock_text;
+static TextLayer *date_text;
+static TextLayer *header_text;
+static TextLayer *mem_text;
+static TextLayer *content_text;
+static TextLayer *batt_text;
+static GFont font;
+static GFont font_small;
+static Layer *background_layer;
+#ifdef PBL_HEALTH
+static TextLayer *steps_text;
+static bool health_available = false;
+#endif
+
+#if PBL_DISPLAY_HEIGHT == 228
+#define FONT_SIZE_LARGE 58
+#define FONT_SIZE_SMALL 16
+#define BORDER_PERCENT 5
+#define HEADER "*** COMMODORE 64 ***"
+#define DATE_FMT "%a %d. %b %Y"
+#define STEPS_FMT "Steps:%d"
+#elif PBL_DISPLAY_HEIGHT == 168
+#define FONT_SIZE_LARGE 48
+#define FONT_SIZE_SMALL 15
+#define BORDER_PERCENT 3
+#define HEADER "* COMMODORE 64 *"
+#define DATE_FMT "%a %d.%b %y"
+#define STEPS_FMT "S:%d"
+#endif
+
+#define FONT2(SIZE) (RESOURCE_ID_FONT_COMMODORE_ ## SIZE)
+#define FONT(SIZE) FONT2(SIZE)
+#define FONT_SMALL FONT(FONT_SIZE_SMALL)
+#define FONT_LARGE FONT(FONT_SIZE_LARGE)
+
+#define PALETTE_COLODORE
+
+#if defined(PALETTE_COLODORE)
+// https://www.colodore.com/
+#define GColor_Red GColorFromHEX(0x813338)
+#define GColor_Blue GColorFromHEX(0x2e2c9b)
+#define GColor_Orange GColorFromHEX(0x8e5029)
+#define GColor_Grey1 GColorFromHEX(0x4a4a4a)
+#define GColor_Grey2 GColorFromHEX(0x7b7b7b)
+#define GColor_LightBlue GColorFromHEX(0x6f6deb)
+#define GColor_LightGrey GColorFromHEX(0xb2b2b2)
+#elif defined(PALETTE_PEPTO)
+// /usr/share/vice/C64/pepto-pal.vpl
+#define GColor_Red GColorFromHEX(0x68372b)
+#define GColor_Blue GColorFromHEX(0x352879)
+#define GColor_Orange GColorFromHEX(0x6f4f25)
+#define GColor_Grey1 GColorFromHEX(0x444444)
+#define GColor_Grey2 GColorFromHEX(0x6c6c6c)
+#define GColor_LightBlue GColorFromHEX(0x6c5eb5)
+#define GColor_LightGrey GColorFromHEX(0x959595)
+#elif defined(PALETTE_WIKIPEDIA)
+// https://en.wikipedia.org/wiki/Commodore_64#Graphics
+#define GColor_Red GColorFromHEX(0x9F4E44)
+#define GColor_Blue GColorFromHEX(0x50459B)
+#define GColor_Orange GColorFromHEX(0xA1683C)
+#define GColor_Grey1 GColorFromHEX(0x626262)
+#define GColor_Grey2 GColorFromHEX(0x898989)
+#define GColor_LightBlue GColorFromHEX(0x887ECB)
+#define GColor_LightGrey GColorFromHEX(0xADADAD)
+#endif
+
+#if defined(PBL_COLOR)
+#define GColor_Border GColor_LightBlue
+#define GColor_Background GColor_Blue
+#define GColor_Text GColor_LightBlue
+#define GColor_Clock GColor_LightGrey
+#define GColor_Bat_Med GColor_Orange
+#define GColor_Bat_Low GColor_Red
+#elif defined(PBL_BW)
+#define GColor_Border GColorWhite
+#define GColor_Background GColorBlack
+#define GColor_Text GColorWhite
+#define GColor_Clock GColorWhite
+#define GColor_Bat_Med GColorWhite
+#define GColor_Bat_Low GColorWhite
+#endif
+
+
+static void background_update_proc(Layer *layer, GContext *ctx) {
+ GRect bounds = layer_get_bounds(layer);
+ graphics_context_set_fill_color(ctx, GColor_Background);
+ graphics_fill_rect(ctx, bounds, 0, GCornerNone);
+}
+
+static void main_window_load(Window *window) {
+ Layer *window_layer = window_get_root_layer(window);
+ GRect bounds = layer_get_unobstructed_bounds(window_layer);
+ font = fonts_load_custom_font(resource_get_handle(FONT_LARGE));
+ font_small = fonts_load_custom_font(resource_get_handle(FONT_SMALL));
+
+ /* inner background */
+ int16_t border_width = bounds.size.w * BORDER_PERCENT / 100;
+ GRect bg_bounds = GRect(border_width, border_width, bounds.size.w - 2*border_width, bounds.size.h - 2*border_width);
+ background_layer = layer_create(bg_bounds);
+ layer_set_update_proc(background_layer, background_update_proc);
+ layer_add_child(window_layer, background_layer);
+
+ /* header text */
+ header_text = text_layer_create(GRect(bg_bounds.origin.x, bg_bounds.origin.y, bg_bounds.size.w, FONT_SIZE_SMALL+2));
+ text_layer_set_background_color(header_text, GColorClear);
+ text_layer_set_text_color(header_text, GColor_Text);
+ text_layer_set_text_alignment(header_text, GTextAlignmentCenter);
+ text_layer_set_font(header_text, font_small);
+ text_layer_set_text(header_text, HEADER);
+ layer_add_child(window_layer, text_layer_get_layer(header_text));
+
+ /* free memory text */
+ mem_text = text_layer_create(GRect(bg_bounds.origin.x, bg_bounds.origin.y + FONT_SIZE_SMALL, bg_bounds.size.w, FONT_SIZE_SMALL+2));
+ text_layer_set_background_color(mem_text, GColorClear);
+ text_layer_set_text_color(mem_text, GColor_Text);
+ text_layer_set_text_alignment(mem_text, GTextAlignmentCenter);
+ text_layer_set_font(mem_text, font_small);
+ static char mem_buffer[20];
+ snprintf(mem_buffer, sizeof(mem_buffer), "%d bytes free", heap_bytes_free());
+ text_layer_set_text(mem_text, mem_buffer);
+ layer_add_child(window_layer, text_layer_get_layer(mem_text));
+
+ /* content text */
+ content_text = text_layer_create(GRect(bg_bounds.origin.x, bg_bounds.origin.y + 3*FONT_SIZE_SMALL, bg_bounds.size.w, FONT_SIZE_SMALL+2));
+ text_layer_set_background_color(content_text, GColorClear);
+ text_layer_set_text_color(content_text, GColor_Text);
+ text_layer_set_text_alignment(content_text, GTextAlignmentLeft);
+ text_layer_set_font(content_text, font_small);
+ text_layer_set_text(content_text, "READY.");
+ layer_add_child(window_layer, text_layer_get_layer(content_text));
+
+ /* clock text */
+ clock_text = text_layer_create(GRect(bg_bounds.origin.x, bounds.size.h/2 - FONT_SIZE_LARGE/2, bg_bounds.size.w, FONT_SIZE_LARGE+2));
+ text_layer_set_background_color(clock_text, GColorClear);
+ text_layer_set_text_color(clock_text, GColor_Clock);
+ text_layer_set_text_alignment(clock_text, GTextAlignmentCenter);
+ text_layer_set_font(clock_text, font);
+ layer_add_child(window_layer, text_layer_get_layer(clock_text));
+
+ /* date text */
+ date_text = text_layer_create(GRect(bg_bounds.origin.x, bounds.size.h/2 + FONT_SIZE_LARGE/2 + FONT_SIZE_SMALL, bg_bounds.size.w, FONT_SIZE_SMALL+2));
+ text_layer_set_background_color(date_text, GColorClear);
+ text_layer_set_text_color(date_text, GColor_Text);
+ text_layer_set_text_alignment(date_text, GTextAlignmentCenter);
+ text_layer_set_font(date_text, font_small);
+ layer_add_child(window_layer, text_layer_get_layer(date_text));
+
+#ifdef PBL_HEALTH
+ /* step counter text */
+ steps_text = text_layer_create(GRect(bg_bounds.origin.x, bg_bounds.origin.y + bg_bounds.size.h - FONT_SIZE_SMALL - 2, bg_bounds.size.w, FONT_SIZE_SMALL+2));
+ text_layer_set_background_color(steps_text, GColorClear);
+ text_layer_set_text_color(steps_text, GColor_Text);
+ text_layer_set_text_alignment(steps_text, GTextAlignmentLeft);
+ text_layer_set_font(steps_text, font_small);
+ layer_add_child(window_layer, text_layer_get_layer(steps_text));
+#endif
+
+ /* battery text */
+ batt_text = text_layer_create(GRect(bg_bounds.origin.x, bg_bounds.origin.y + bg_bounds.size.h - FONT_SIZE_SMALL - 2, bg_bounds.size.w, FONT_SIZE_SMALL+2));
+ text_layer_set_background_color(batt_text, GColorClear);
+ text_layer_set_text_color(batt_text, GColor_Text);
+ text_layer_set_text_alignment(batt_text, GTextAlignmentRight);
+ text_layer_set_font(batt_text, font_small);
+ layer_add_child(window_layer, text_layer_get_layer(batt_text));
+}
+
+static void main_window_unload(Window *window) {
+ text_layer_destroy(content_text);
+ text_layer_destroy(header_text);
+ text_layer_destroy(mem_text);
+ text_layer_destroy(clock_text);
+ text_layer_destroy(date_text);
+ text_layer_destroy(batt_text);
+#ifdef PBL_HEALTH
+ text_layer_destroy(steps_text);
+#endif
+ layer_destroy(background_layer);
+ fonts_unload_custom_font(font);
+ fonts_unload_custom_font(font_small);
+}
+
+static void update_time() {
+ time_t temp = time(NULL);
+ struct tm *tick_time = localtime(&temp);
+
+ static char buffer[8];
+ if (clock_is_24h_style()) {
+ strftime(buffer, sizeof(buffer), "%H:%M", tick_time);
+ } else {
+ strftime(buffer, sizeof(buffer), "%I:%M", tick_time);
+ }
+
+ text_layer_set_text(clock_text, buffer);
+}
+
+static void update_date() {
+ time_t temp = time(NULL);
+ struct tm *tick_time = localtime(&temp);
+
+ static char buffer[20];
+ strftime(buffer, sizeof(buffer), DATE_FMT, tick_time);
+
+ text_layer_set_text(date_text, buffer);
+}
+
+static void tick_handler(struct tm *tick_time, TimeUnits units_changed) {
+ if (units_changed & MINUTE_UNIT) {
+ update_time();
+ }
+ if (units_changed & DAY_UNIT) {
+ update_date();
+ }
+}
+
+static void battery_handler(BatteryChargeState charge) {
+ static char buffer[12];
+ const char *charge_indicator = charge.is_charging ? "^" : "";
+ snprintf(buffer, sizeof(buffer), "%sBat:%d%%", charge_indicator, charge.charge_percent);
+ if (charge.charge_percent <= 20) {
+ text_layer_set_text_color(batt_text, GColor_Bat_Med);
+ } else if (charge.charge_percent <= 10) {
+ text_layer_set_text_color(batt_text, GColor_Bat_Low);
+ } else {
+ text_layer_set_text_color(batt_text, GColor_Text);
+ }
+ text_layer_set_text(batt_text, buffer);
+}
+
+#ifdef PBL_HEALTH
+static void health_handler(HealthEventType event, void *context) {
+ static char buffer[24];
+ HealthValue steps;
+
+ switch (event) {
+ case HealthEventSignificantUpdate:
+ case HealthEventMovementUpdate:
+ steps = health_service_sum_today(HealthMetricStepCount);
+ snprintf(buffer, sizeof(buffer), STEPS_FMT, (int) steps);
+ text_layer_set_text(steps_text, buffer);
+ break;
+ default:
+ break;
+ }
+}
+#endif
+
+static void init() {
+ main_window = window_create();
+ window_set_window_handlers(main_window, (WindowHandlers) {
+ .load = main_window_load,
+ .unload = main_window_unload,
+ });
+ window_set_background_color(main_window, GColor_Border);
+ window_stack_push(main_window, true);
+
+ tick_timer_service_subscribe(MINUTE_UNIT | DAY_UNIT, tick_handler);
+ battery_state_service_subscribe(battery_handler);
+#ifdef PBL_HEALTH
+ health_available = health_service_events_subscribe(health_handler, NULL);
+#endif
+
+ /* initialize dynamic texts */
+ update_time();
+ update_date();
+ battery_handler(battery_state_service_peek());
+}
+
+static void deinit() {
+#ifdef PBL_HEALTH
+ health_service_events_unsubscribe();
+#endif
+ battery_state_service_unsubscribe();
+ tick_timer_service_unsubscribe();
+ window_destroy(main_window);
+}
+
+int main() {
+ init();
+ app_event_loop();
+ deinit();
+}