···11#include "clock_tid.h"
22#include <pebble.h>
33-#include <string.h> // For memcpy, memset
43#include <stdint.h> // For uint types
55-#include <stdio.h> // For snprintf
44+#include "text_layer_util.h"
55+#include <stdlib.h> // For rand()
6677-// --- Static variables specific to TID Clock ---
87static const char S32_CHAR[] = "234567abcdefghijklmnopqrstuvwxyz";
99-#define S32_CHAR_LEN (sizeof(S32_CHAR) - 1)
1010-static uint64_t last_timestamp = 0;
1111-// Buffer needs to hold 11 (timestamp) + 2 (clock ID) + 1 (null terminator) = 14 chars
1212-static char s_tid_buffer[14]; // Buffer for TID time string "MMMSS\0"
1313-// Remove cache for the old 5-digit display (variable is now unused)
1414-// static int last_tid_time = -1;
88+static uint64_t last_timestamp;
99+static char s_tid_buffer[14];
15101616-// --- Static helper functions ---
1111+// Generate monotonic TID string into tid_buffer
1212+void clock_tid_get_string(char *tid_buffer, size_t tid_buffer_len, time_t seconds, uint16_t milliseconds) {
1313+ if (tid_buffer_len < sizeof(s_tid_buffer)) return;
17141818-static char* s32encode_c(uint64_t i, char *buffer, size_t buffer_len) {
1919- if (buffer_len == 0) return NULL;
2020- char *ptr = buffer + buffer_len;
2121- if (i == 0) {
2222- if (ptr > buffer) { *(--ptr) = S32_CHAR[0]; return ptr; }
2323- else { return NULL; }
1515+ uint64_t current_micros = (uint64_t)seconds * 1000000 + (uint64_t)milliseconds * 1000;
1616+ if (current_micros <= last_timestamp) {
1717+ current_micros = last_timestamp + 1;
1818+ }
1919+ last_timestamp = current_micros;
2020+2121+ // Encode 11-char base-32 timestamp (pad with '2')
2222+ size_t pos = 11;
2323+ for (size_t i = 0; i < pos; ++i) {
2424+ tid_buffer[i] = S32_CHAR[0];
2425 }
2525- while (i > 0 && ptr > buffer) {
2626- uint8_t remainder = i & 31;
2727- i >>= 5;
2828- *(--ptr) = S32_CHAR[remainder];
2626+ uint64_t v = current_micros;
2727+ while (v && pos) {
2828+ tid_buffer[--pos] = S32_CHAR[v & 31];
2929+ v >>= 5;
2930 }
3030- return ptr;
3131-}
32313333-static void createRaw_c(uint64_t timestamp, char *tid_buffer, size_t tid_buffer_len) {
3434- if (tid_buffer_len < 14) return;
3535-3636- char ts_temp_buffer[11];
3737- char* encoded_ts_start = s32encode_c(timestamp, ts_temp_buffer, sizeof(ts_temp_buffer));
3838- size_t encoded_ts_len = encoded_ts_start ? (ts_temp_buffer + sizeof(ts_temp_buffer)) - encoded_ts_start : 0;
3939-4040- memset(tid_buffer, S32_CHAR[0], 11);
4141- if (encoded_ts_len > 0 && encoded_ts_len <= 11) {
4242- memcpy(tid_buffer + (11 - encoded_ts_len), encoded_ts_start, encoded_ts_len);
4343- } else if (encoded_ts_len > 11) {
4444- memcpy(tid_buffer, encoded_ts_start + (encoded_ts_len - 11), 11);
3232+ // Encode 2-char random clock ID (0-1023)
3333+ uint16_t cid = (uint16_t)(rand() % 1024);
3434+ pos = 2;
3535+ for (size_t i = 0; i < pos; ++i) {
3636+ tid_buffer[11 + i] = S32_CHAR[0];
3737+ }
3838+ uint16_t v2 = cid;
3939+ while (v2 && pos) {
4040+ tid_buffer[11 + --pos] = S32_CHAR[v2 & 31];
4141+ v2 >>= 5;
4542 }
46434747- tid_buffer[11] = S32_CHAR[0];
4848- tid_buffer[12] = S32_CHAR[0];
4944 tid_buffer[13] = '\0';
5045}
51465252-/**
5353- * Generates a TID string for the given time into the provided buffer.
5454- * Ensures monotonicity using a static variable internally.
5555- */
5656-void clock_tid_get_string(char *tid_buffer, size_t tid_buffer_len, time_t seconds, uint16_t milliseconds) {
5757- if (tid_buffer_len < 14) return; // Ensure buffer is large enough
5858-5959- uint64_t current_micros = (uint64_t)seconds * 1000000 + (uint64_t)milliseconds * 1000;
6060-6161- if (current_micros <= last_timestamp) {
6262- current_micros = last_timestamp + 1;
6363- }
6464- last_timestamp = current_micros;
6565- createRaw_c(current_micros, tid_buffer, tid_buffer_len);
6666-}
6767-6847// --- Pebble UI Interface Functions ---
69487049TextLayer* clock_tid_init(GRect bounds, Layer *window_layer) {
7171- TextLayer* layer = text_layer_create(bounds);
7272- text_layer_set_background_color(layer, GColorClear);
7373- text_layer_set_text_color(layer, GColorBlack);
7474- text_layer_set_text(layer, "-----"); // Initial placeholder
7575- #define TID_FONT FONT_KEY_GOTHIC_18_BOLD // Keep font definitions near usage
7676- text_layer_set_font(layer, fonts_get_system_font(TID_FONT));
7777- text_layer_set_text_alignment(layer, GTextAlignmentCenter);
7878- layer_add_child(window_layer, text_layer_get_layer(layer));
7979- return layer;
5050+ return text_layer_util_create(bounds, window_layer, "-----", FONT_KEY_GOTHIC_18_BOLD);
8051}
81528253void clock_tid_deinit(TextLayer *layer) {
+19
src/c/text_layer_util.h
···11+#pragma once
22+33+#include <pebble.h>
44+55+// A helper to create a TextLayer configured with standard styling and add it to a parent layer.
66+// bounds: frame of the TextLayer
77+// parent: parent Layer to add this TextLayer to
88+// init_text: initial text to display
99+// font_key: key of the system font, e.g. FONT_KEY_GOTHIC_24_BOLD
1010+static inline TextLayer* text_layer_util_create(GRect bounds, Layer* parent, const char* init_text, const char* font_key) {
1111+ TextLayer* layer = text_layer_create(bounds);
1212+ text_layer_set_background_color(layer, GColorClear);
1313+ text_layer_set_text_color(layer, GColorBlack);
1414+ text_layer_set_text(layer, init_text);
1515+ text_layer_set_font(layer, fonts_get_system_font(font_key));
1616+ text_layer_set_text_alignment(layer, GTextAlignmentCenter);
1717+ layer_add_child(parent, text_layer_get_layer(layer));
1818+ return layer;
1919+}
+13-73
src/c/watchface.c
···22#include <stdlib.h>
33#include <time.h>
4455-// Enable logging - Removed incorrect define, APP_LOG is in pebble.h
66-77-// --- Clock Module Includes ---
55+// Clock modules
86#include "clock_beat.h"
99-// #include "clock_noonzone.h"
107#include "clock_closest_noon.h"
118#include "clock_tid.h"
129#include "clock_decimal.h"
13101411// --- Window and Layer Globals ---
1512static Window *s_main_window;
1616-static TextLayer *s_beat_layer; // Layer for Swatch Beat Time
1717-static TextLayer *s_tid_layer; // Layer for TID Time
1818-// static TextLayer *s_noonzone_layer; // Layer for Noon Zone Time // Removed
1919-static TextLayer *s_closest_noon_layer; // Layer for Closest-to-Noon TZ
2020-static TextLayer *s_decimal_layer; // Layer for Decimal Time
1313+static TextLayer *s_beat_layer;
1414+static TextLayer *s_tid_layer;
1515+static TextLayer *s_closest_noon_layer;
1616+static TextLayer *s_decimal_layer;
21172218// --- Pebble Window Management ---
23192420// Handles updates from the TickTimerService
2521static void tick_handler(struct tm *tick_time, TimeUnits units_changed) {
2626- // Get time once for both updates
2722 time_t seconds;
2823 uint16_t milliseconds;
2924 time_ms(&seconds, &milliseconds);
···3126 // Update both time displays
3227 clock_beat_update(s_beat_layer, seconds);
3328 clock_tid_update(s_tid_layer, seconds, milliseconds);
3434- // clock_noonzone_update(s_noonzone_layer, seconds); // Removed
3529 clock_closest_noon_update(s_closest_noon_layer, seconds);
3630 clock_decimal_update(s_decimal_layer, seconds);
3731}
···3933static void main_window_load(Window *window) {
4034 Layer *window_layer = window_get_root_layer(window);
4135 GRect bounds = layer_get_bounds(window_layer);
4242- APP_LOG(APP_LOG_LEVEL_DEBUG, "Bounds: H=%d, W=%d", bounds.size.h, bounds.size.w);
4343-4444- // Define layout constants for 4 layers
4545- const int num_layers = 4;
4646- const int16_t v_padding = 2; // Slightly more padding maybe
4747- // Calculate available height for layers after removing padding
4848- const int16_t total_available_h = bounds.size.h - (num_layers - 1) * v_padding;
4949- // Distribute height equally among layers
5050- const int16_t layer_h = total_available_h / num_layers;
5151- APP_LOG(APP_LOG_LEVEL_DEBUG, "Total Available H: %d, Layer H: %d", total_available_h, layer_h);
5252- // Verify calculation result
5353- if (layer_h <= 0) {
5454- APP_LOG(APP_LOG_LEVEL_ERROR, "Error: Calculated Layer Height <= 0");
5555- // Handle error or default to a minimal height
5656- // layer_h = 10; // Example fallback
5757- }
5858-5959- // Font sizes (adjust as needed - removed noonzone)
6060- #define BEAT_FONT FONT_KEY_GOTHIC_24_BOLD
6161- // #define NOONZONE_FONT FONT_KEY_GOTHIC_18_BOLD // Removed
6262- #define CLOSEST_FONT FONT_KEY_GOTHIC_18_BOLD
6363- #define TID_FONT FONT_KEY_GOTHIC_18_BOLD
6464- #define DECIMAL_FONT FONT_KEY_GOTHIC_18_BOLD
6565-6666- // Calculate Y positions based on new layout
6767- // Beat: Top
6868- int16_t beat_y = 0;
6969-7070- // TID: Bottom
7171- int16_t tid_y = bounds.size.h - layer_h;
3636+ const int16_t total_h = bounds.size.h;
3737+ const int16_t layer_h = total_h / 4;
72387373- // Closest Noon: Above TID
7474- int16_t closest_y = tid_y - layer_h - v_padding;
7575-7676- // Decimal: Halfway between bottom of Beat and top of Closest Noon
7777- // Bottom of Beat = beat_y + layer_h = layer_h
7878- // Top of Closest Noon = closest_y
7979- // Midpoint = (bottom_of_beat + top_of_closest_noon) / 2
8080- // Adjust for decimal layer height: Midpoint - layer_h / 2
8181- int16_t decimal_midpoint = (layer_h + closest_y) / 2;
8282- int16_t decimal_y = decimal_midpoint - (layer_h / 2);
8383-8484- APP_LOG(APP_LOG_LEVEL_DEBUG, "Y Positions: Beat=%d, Decimal=%d, Closest=%d, TID=%d", beat_y, decimal_y, closest_y, tid_y);
8585-8686- // Create Layers in new order
8787- // Beat Time TextLayer (Top)
8888- s_beat_layer = clock_beat_init(GRect(0, beat_y, bounds.size.w, layer_h), window_layer);
8989-9090- // Decimal Time TextLayer (Middle)
9191- s_decimal_layer = clock_decimal_init(GRect(0, decimal_y, bounds.size.w, layer_h), window_layer);
9292-9393- // Closest Noon Time TextLayer (Middle-Bottom)
9494- s_closest_noon_layer = clock_closest_noon_init(GRect(0, closest_y, bounds.size.w, layer_h), window_layer);
9595-9696- // TID TextLayer (Bottom)
9797- s_tid_layer = clock_tid_init(GRect(0, tid_y, bounds.size.w, layer_h), window_layer);
9898-9999- // // Create Noon Zone Time TextLayer // Removed
100100- // s_noonzone_layer = clock_noonzone_init(GRect(0, noonzone_y, bounds.size.w, layer_h), window_layer);
101101-3939+ // Create layers
4040+ s_beat_layer = clock_beat_init(GRect(0, 0, bounds.size.w, layer_h), window_layer);
4141+ s_decimal_layer = clock_decimal_init(GRect(0, layer_h, bounds.size.w, layer_h), window_layer);
4242+ s_closest_noon_layer = clock_closest_noon_init(GRect(0, 2 * layer_h, bounds.size.w, layer_h), window_layer);
4343+ s_tid_layer = clock_tid_init(GRect(0, 3 * layer_h, bounds.size.w, layer_h), window_layer);
10244}
1034510446static void main_window_unload(Window *window) {
10547 // Destroy TextLayers
10648 clock_beat_deinit(s_beat_layer);
107107- // clock_noonzone_deinit(s_noonzone_layer); // Removed
10849 clock_closest_noon_deinit(s_closest_noon_layer);
10950 clock_tid_deinit(s_tid_layer);
110110- clock_decimal_deinit(s_decimal_layer);
5151+ clock_decimal_deinit(s_decimal_layer);
11152}
1125311354static void init() {
114114- // Seed random number generator (used by closest noon clock)
11555 srand(time(NULL));
1165611757 // Create main Window element and assign to pointer