16 Commits
15 changed files with 47 additions and 158 deletions
+4 -2
View File
@@ -1,3 +1,5 @@
/.clangd
/build /build
/.lock* /.clangd
/.lock-waf*
/package-lock.json
/node_modules
+1 -1
View File
@@ -1,6 +1,6 @@
MIT License MIT License
Copyright (c) 2026 hilariouspig Copyright (c) 2026 RandyTheSilly
THIS LICENSE DOES NOT APPLY TO ANY ".png" NOR ".ttf" FILES IN THIS REPOSITORY. THIS LICENSE DOES NOT APPLY TO ANY ".png" NOR ".ttf" FILES IN THIS REPOSITORY.
-7
View File
@@ -20,10 +20,3 @@ Since the original watchface is **NOT** open source, this "port" could be more a
- Add more Maan art (see [this gallery](https://www.flickr.com/photos/53297919@N04/albums/72157624708812279) for potential candidates) - Add more Maan art (see [this gallery](https://www.flickr.com/photos/53297919@N04/albums/72157624708812279) for potential candidates)
- Potential light mode (don't hold your breath - I'm still not sure this would look good and I don't like the idea of having settings) - Potential light mode (don't hold your breath - I'm still not sure this would look good and I don't like the idea of having settings)
- Black&white support if I get bored or Pebble releases a modern b&w watch (Flint doesn't count) - Black&white support if I get bored or Pebble releases a modern b&w watch (Flint doesn't count)
# Development
1. Install the [Pebble SDK](https://developer.repebble.com/sdk/)
2. Clone this repo
3. `pebble build` (some headers needed to satisfy your IDE are generated on first build)
4. If using `clangd`, run `./tools/clangd.zsh` to generate a `.clangd` file to fix various error notifications
5. Hack away and test with `pebble build && pebble install --emulator emery`
-46
View File
@@ -1,46 +0,0 @@
/*
* Example jshint configuration file for Pebble development.
*
* Check out the full documentation at http://www.jshint.com/docs/options/
*/
{
// Declares the existence of the globals available in PebbleKit JS.
"globals": {"console": true, "setTimeout": true, "setInterval": true, "Int8Array": true, "Uint8Array": true, "Uint8ClampedArray": true, "Int16Array": true, "Uint16Array": true, "Int32Array": true, "Uint32Array": true, "Float32Array": true, "Float64Array": true, "Pebble": true, "WebSocket": true, "XMLHttpRequest": true, "navigator": true, "localStorage": true, "require": true, "exports": true, "module": true},
// Do not mess with standard JavaScript objects (Array, Date, etc)
"freeze": true,
// Do not use eval! Keep this warning turned on (ie: false)
"evil": false,
/*
* The options below are more style/developer dependent.
* Customize to your liking.
*/
// All variables should be in camelcase - too specific for CloudPebble builds to fail
// "camelcase": true,
// Do not allow blocks without { } - too specific for CloudPebble builds to fail.
// "curly": true,
// Prohibits the use of immediate function invocations without wrapping them in parentheses
"immed": true,
// Don't enforce indentation, because it's not worth failing builds over
// (especially given our somewhat lacklustre support for it)
"indent": false,
// Do not use a variable before it's defined
"latedef": "nofunc",
// Spot undefined variables
"undef": "true",
// Spot unused variables
"unused": "true",
// Enable ES6 syntax (const, let, arrow functions, etc)
"esversion": 6
}
+13 -3
View File
@@ -1,7 +1,11 @@
{ {
"author": "hilariouspig", "author": "RandyTheSilly",
"dependencies": {}, "dependencies": {},
"keywords": [], "keywords": [
"silly",
"creature",
"swap"
],
"name": "maan-on-my-wrist-xl", "name": "maan-on-my-wrist-xl",
"pebble": { "pebble": {
"displayName": "Maan On My Wrist XL", "displayName": "Maan On My Wrist XL",
@@ -712,6 +716,12 @@
], ],
"type": "bitmap" "type": "bitmap"
}, },
{
"file": "images/menu_icon.png",
"name": "MENU_ICON",
"menuIcon": true,
"type": "bitmap"
},
{ {
"characterRegex": "[:0-9]", "characterRegex": "[:0-9]",
"file": "fonts/RetroComputerTime.ttf", "file": "fonts/RetroComputerTime.ttf",
@@ -746,5 +756,5 @@
"watchface": true "watchface": true
} }
}, },
"version": "1.0.0" "version": "1.1.0"
} }
Binary file not shown.

Before

Width:  |  Height:  |  Size: 465 B

After

Width:  |  Height:  |  Size: 490 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 425 B

After

Width:  |  Height:  |  Size: 464 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 448 B

After

Width:  |  Height:  |  Size: 485 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 146 B

+26 -28
View File
@@ -69,7 +69,8 @@ static GRect s_date_grect_b;
static GRect s_bt_grect_b; static GRect s_bt_grect_b;
// declare animation templates // declare animation templates
static uint8_t s_animation_duration = 255; static const uint8_t s_animation_duration = 255;
static bool s_first_tick_occurred = false;
static Animation *s_head_raise_template; static Animation *s_head_raise_template;
static Animation *s_head_out_template; static Animation *s_head_out_template;
static Animation *s_head_in_template; static Animation *s_head_in_template;
@@ -128,8 +129,6 @@ static void destroy_animation_handler(Animation *animation, bool finished, void
animation_destroy(animation); animation_destroy(animation);
} }
static bool first_minute_update = true;
static void update_minute_1() { static void update_minute_1() {
// get a tm structure // get a tm structure
time_t temp = time(NULL); time_t temp = time(NULL);
@@ -163,7 +162,7 @@ static void update_minute_1() {
//// calculate new positions //// calculate new positions
s_date_grect_b = GRect(s_ones_offset[ones], s_date_pos_y, s_date_bt_width, s_date_height); s_date_grect_b = GRect(s_ones_offset[ones], s_date_pos_y, s_date_bt_width, s_date_height);
s_bt_grect_b = GRect(s_bt_pos_x - s_ones_offset[ones], s_bt_pos_y, s_date_bt_width, s_bt_height); s_bt_grect_b = GRect(s_bt_pos_x - s_ones_offset[ones], s_bt_pos_y, s_date_bt_width, s_bt_height);
if (!first_minute_update) { if (s_first_tick_occurred) { // no need to animate before first tick, as positions are set in init() without animating
//// configure animations //// configure animations
////// date ////// date
PropertyAnimation *date_center_prop = property_animation_create_layer_frame(text_layer_get_layer(s_date_layer), &s_date_grect_a, &s_date_grect_b); PropertyAnimation *date_center_prop = property_animation_create_layer_frame(text_layer_get_layer(s_date_layer), &s_date_grect_a, &s_date_grect_b);
@@ -186,7 +185,6 @@ static void update_minute_1() {
s_date_grect_a = s_date_grect_b; s_date_grect_a = s_date_grect_b;
s_bt_grect_a = s_bt_grect_b; s_bt_grect_a = s_bt_grect_b;
} }
first_minute_update = false;
// DEBUG memory usage layer // DEBUG memory usage layer
// static char s_memory_buffer[32]; // static char s_memory_buffer[32];
@@ -215,8 +213,8 @@ static void update_minute_30_out_handler(Animation *animation, bool finished, vo
s_butt = gbitmap_create_with_resource(s_random_butts[s_butt_index]); s_butt = gbitmap_create_with_resource(s_random_butts[s_butt_index]);
// swap colors // swap colors
replace_gbitmap_color(GColorGreen, s_random_color_next, s_butt, NULL); replace_gbitmap_color(GColorGreen, s_random_color_next, s_butt);
replace_gbitmap_color(s_random_color_current, s_random_color_next, s_head, NULL); replace_gbitmap_color(s_random_color_current, s_random_color_next, s_head);
s_random_color_current = s_random_color_next; s_random_color_current = s_random_color_next;
s_random_color_next = s_dark_bg_colors[rand() % s_color_count]; s_random_color_next = s_dark_bg_colors[rand() % s_color_count];
@@ -225,11 +223,6 @@ static void update_minute_30_out_handler(Animation *animation, bool finished, vo
bitmap_layer_set_bitmap(s_guy_head_layer, s_head); bitmap_layer_set_bitmap(s_guy_head_layer, s_head);
} }
static void update_minute_30() {
update_minute_1();
schedule_guy_animation(s_butt_raise_template, s_butt_out_template, s_butt_in_template, update_minute_30_out_handler);
}
static void update_minute_60_out_handler(Animation *animation, bool finished, void *context) { static void update_minute_60_out_handler(Animation *animation, bool finished, void *context) {
// destroy and reassign // destroy and reassign
gbitmap_destroy(s_head); gbitmap_destroy(s_head);
@@ -237,8 +230,8 @@ static void update_minute_60_out_handler(Animation *animation, bool finished, vo
s_head = gbitmap_create_with_resource(s_random_heads[s_head_index]); s_head = gbitmap_create_with_resource(s_random_heads[s_head_index]);
// swap colors // swap colors
replace_gbitmap_color(GColorGreen, s_random_color_next, s_head, NULL); replace_gbitmap_color(GColorGreen, s_random_color_next, s_head);
replace_gbitmap_color(s_random_color_current, s_random_color_next, s_butt, NULL); replace_gbitmap_color(s_random_color_current, s_random_color_next, s_butt);
s_random_color_current = s_random_color_next; s_random_color_current = s_random_color_next;
s_random_color_next = s_dark_bg_colors[rand() % s_color_count]; s_random_color_next = s_dark_bg_colors[rand() % s_color_count];
@@ -247,18 +240,17 @@ static void update_minute_60_out_handler(Animation *animation, bool finished, vo
bitmap_layer_set_bitmap(s_guy_butt_layer, s_butt); bitmap_layer_set_bitmap(s_guy_butt_layer, s_butt);
} }
static void update_minute_60() {
update_minute_1();
schedule_guy_animation(s_head_raise_template, s_head_out_template, s_head_in_template, update_minute_60_out_handler);
}
static void minute_handler(struct tm *tick_time, TimeUnits units_changed) { static void minute_handler(struct tm *tick_time, TimeUnits units_changed) {
update_minute_1();
if (!s_first_tick_occurred) {
s_first_tick_occurred = true;
return; // do not animate part swaps on first tick
}
if (tick_time->tm_min % 60 == 0) { if (tick_time->tm_min % 60 == 0) {
update_minute_60(); schedule_guy_animation(s_head_raise_template, s_head_out_template, s_head_in_template, update_minute_60_out_handler);
} else if (tick_time->tm_min % 30 == 0) { } else if (tick_time->tm_min % 30 == 0) {
update_minute_30(); schedule_guy_animation(s_butt_raise_template, s_butt_out_template, s_butt_in_template, update_minute_30_out_handler);
} else {
update_minute_1();
} }
} }
@@ -353,13 +345,13 @@ static void bluetooth_callback(bool connected) {
// DEBUG buttons // DEBUG buttons
// static void up_single_click_handler(ClickRecognizerRef recognizer, void *context) { // static void up_single_click_handler(ClickRecognizerRef recognizer, void *context) {
// Window *window = (Window *)context; // Window *window = (Window *)context;
// update_minute_60(); // schedule_guy_animation(s_head_raise_template, s_head_out_template, s_head_in_template, update_minute_60_out_handler);
//} //}
// DEBUG buttons // DEBUG buttons
// static void down_single_click_handler(ClickRecognizerRef recognizer, void *context) { // static void down_single_click_handler(ClickRecognizerRef recognizer, void *context) {
// Window *window = (Window *)context; // Window *window = (Window *)context;
// update_minute_30(); // schedule_guy_animation(s_butt_raise_template, s_butt_out_template, s_butt_in_template, update_minute_30_out_handler);
//} //}
// DEBUG buttons // DEBUG buttons
@@ -371,7 +363,13 @@ static void bluetooth_callback(bool connected) {
// set up the app on launch (don't put app logic in here); // set up the app on launch (don't put app logic in here);
static void init() { static void init() {
// seed RNG // seed RNG
srand(time(NULL)); uint16_t seed_ms;
time_t seed_time;
time_ms(&seed_time, &seed_ms);
srand((unsigned int)seed_time ^ seed_ms);
rand();
rand();
rand(); // throw away first few values
// create main Window element and assign to pointer // create main Window element and assign to pointer
s_main_window = window_create(); s_main_window = window_create();
@@ -399,8 +397,8 @@ static void init() {
// set starting colors // set starting colors
s_random_color_next = s_dark_bg_colors[rand() % s_color_count]; s_random_color_next = s_dark_bg_colors[rand() % s_color_count];
replace_gbitmap_color(GColorGreen, s_random_color_current, s_head, NULL); replace_gbitmap_color(GColorGreen, s_random_color_current, s_head);
replace_gbitmap_color(GColorGreen, s_random_color_current, s_butt, NULL); replace_gbitmap_color(GColorGreen, s_random_color_current, s_butt);
// load custom font // load custom font
#if PBL_DISPLAY_WIDTH <= 180 #if PBL_DISPLAY_WIDTH <= 180
+2 -12
View File
@@ -2,8 +2,6 @@
#include "palette_manip.h" #include "palette_manip.h"
#ifdef PBL_COLOR
int get_num_palette_colors(GBitmap *b){ int get_num_palette_colors(GBitmap *b){
GBitmapFormat format = gbitmap_get_format(b); GBitmapFormat format = gbitmap_get_format(b);
switch (format) { switch (format) {
@@ -16,7 +14,7 @@ int get_num_palette_colors(GBitmap *b){
} }
} }
void replace_gbitmap_color(GColor color_to_replace, GColor replace_with_color, GBitmap *im, BitmapLayer *bml){ void replace_gbitmap_color(GColor color_to_replace, GColor replace_with_color, GBitmap *im){
//First determine what the number of colors in the palette //First determine what the number of colors in the palette
int num_palette_items = get_num_palette_colors(im); int num_palette_items = get_num_palette_colors(im);
@@ -30,12 +28,4 @@ void replace_gbitmap_color(GColor color_to_replace, GColor replace_with_color, G
current_palette[i].argb = (current_palette[i].argb & 0xC0)| (replace_with_color.argb & 0x3F); current_palette[i].argb = (current_palette[i].argb & 0xC0)| (replace_with_color.argb & 0x3F);
} }
} }
}
//Mark the bitmaplayer dirty
if(bml != NULL){
layer_mark_dirty(bitmap_layer_get_layer(bml));
}
}
#endif
+1 -1
View File
@@ -3,5 +3,5 @@
#include <pebble.h> #include <pebble.h>
#ifdef PBL_COLOR #ifdef PBL_COLOR
void replace_gbitmap_color(GColor color_to_replace, GColor replace_with_color, GBitmap *im, BitmapLayer *bml); void replace_gbitmap_color(GColor color_to_replace, GColor replace_with_color, GBitmap *im);
#endif #endif
-18
View File
@@ -1,18 +0,0 @@
#!/usr/bin/env zsh
echo "CompileFlags:
Add:
[
-DPBL_DISPLAY_WIDTH=200,
-DPBL_DISPLAY_HEIGHT=228,
-xc,
-nostdinc,
-DPBL_COLOR,
-I${HOME}/.pebble-sdk/SDKs/current/sdk-core/pebble/emery/include,
-include/home/cuan/gitrepos/Maan-On-My-Wrist-XL/build/include/message_keys.auto.h,
-I$(pwd)/build/emery,
-include${HOME}/.pebble-sdk/SDKs/current/toolchain/arm-none-eabi/arm-none-eabi/include/stdint.h,
-include${HOME}/.pebble-sdk/SDKs/current/toolchain/arm-none-eabi/arm-none-eabi/include/stdlib.h,
-include${HOME}/.pebble-sdk/SDKs/current/toolchain/arm-none-eabi/arm-none-eabi/include/time.h,
-include${HOME}/.pebble-sdk/SDKs/current/toolchain/arm-none-eabi/lib/gcc/arm-none-eabi/14.2.1/include/stddef.h,
-include${HOME}/.pebble-sdk/SDKs/current/toolchain/arm-none-eabi/lib/gcc/arm-none-eabi/14.2.1/include/stdbool.h,
]" > ./.clangd
-33
View File
@@ -1,33 +0,0 @@
#!/usr/bin/env zsh
# THIS IS A LEGACY TOOL THAT WAS USED FOR THE INITIAL
# ASSET EXTRACTION - ASSETS HAVE SINCE BEEN HEAVILY
# MODIFIED, MAKING THIS SCRIPT PRETTY USELESS
if [[ $# -lt 2 ]]; then
echo "Usage: $0 <dir> <half (top/bottom)>"
exit 1
fi
if [[ $2 == "top" ]]; then
gravity="South"
elif [[ $2 == "bottom" ]]; then
gravity="North"
else
echo "Usage: $0 <dir> <half (top/bottom)>"
exit 1
fi
for file in $1/*; do
magick "$file" -gravity $gravity -chop 0x10 "$file"
magick "$file" -transparent Black PNG24:"$file"
magick "$file" -fill "rgb(0,255,0)" -opaque White "$file"
magick "$file" -gravity Center -background None -compose Copy -extent 260x $file
# Bar is commented out for an experiment with rendering in real time on the watch
#magick "$file" -gravity $gravity -background White -splice 0x15 $file
if [[ $2 == "top" ]]; then
magick "$file" -gravity North -background None -splice 0x41 $file
else
magick "$file" -gravity South -background None -splice 0x41 $file
fi
done
-7
View File
@@ -1,7 +0,0 @@
#!/usr/bin/env zsh
# ALWAYS USE ZOPFLI TO CRUSH PNGS AS MUCH AS POSSIBLE BEFORE SHIPPING
for file in **/*; do
zopflipng -y --iterations=500 --filters=01234mepb --lossy_8bit --lossy_transparent $file $file &
done