Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3591933b5b | ||
|
|
99689a6fe4 | ||
|
|
fd7fd56bf6 | ||
|
|
106698dd5f | ||
|
|
0210c32d01 | ||
|
|
80e0103e69 | ||
|
|
4674338d65 | ||
|
|
1a496c3198 | ||
|
|
6b1f25f569 | ||
|
|
98838d5253 | ||
|
|
db0ed68971 | ||
|
|
f0402e9d45 | ||
|
|
e568c5afea | ||
|
|
8acaefa42e | ||
|
|
d10f5667a9 | ||
|
|
cadf4f5783 |
@@ -1,3 +1,5 @@
|
||||
/.clangd
|
||||
/build
|
||||
/.lock*
|
||||
/.clangd
|
||||
/.lock-waf*
|
||||
/package-lock.json
|
||||
/node_modules
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2026 hilariouspig
|
||||
Copyright (c) 2026 RandyTheSilly
|
||||
|
||||
THIS LICENSE DOES NOT APPLY TO ANY ".png" NOR ".ttf" FILES IN THIS REPOSITORY.
|
||||
|
||||
|
||||
@@ -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)
|
||||
- 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)
|
||||
|
||||
# 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`
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -1,7 +1,11 @@
|
||||
{
|
||||
"author": "hilariouspig",
|
||||
"author": "RandyTheSilly",
|
||||
"dependencies": {},
|
||||
"keywords": [],
|
||||
"keywords": [
|
||||
"silly",
|
||||
"creature",
|
||||
"swap"
|
||||
],
|
||||
"name": "maan-on-my-wrist-xl",
|
||||
"pebble": {
|
||||
"displayName": "Maan On My Wrist XL",
|
||||
@@ -712,6 +716,12 @@
|
||||
],
|
||||
"type": "bitmap"
|
||||
},
|
||||
{
|
||||
"file": "images/menu_icon.png",
|
||||
"name": "MENU_ICON",
|
||||
"menuIcon": true,
|
||||
"type": "bitmap"
|
||||
},
|
||||
{
|
||||
"characterRegex": "[:0-9]",
|
||||
"file": "fonts/RetroComputerTime.ttf",
|
||||
@@ -746,5 +756,5 @@
|
||||
"watchface": true
|
||||
}
|
||||
},
|
||||
"version": "1.0.0"
|
||||
"version": "1.1.0"
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 465 B After Width: | Height: | Size: 490 B |
|
Before Width: | Height: | Size: 425 B After Width: | Height: | Size: 464 B |
|
Before Width: | Height: | Size: 448 B After Width: | Height: | Size: 485 B |
|
After Width: | Height: | Size: 146 B |
@@ -69,7 +69,8 @@ static GRect s_date_grect_b;
|
||||
static GRect s_bt_grect_b;
|
||||
|
||||
// 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_out_template;
|
||||
static Animation *s_head_in_template;
|
||||
@@ -128,8 +129,6 @@ static void destroy_animation_handler(Animation *animation, bool finished, void
|
||||
animation_destroy(animation);
|
||||
}
|
||||
|
||||
static bool first_minute_update = true;
|
||||
|
||||
static void update_minute_1() {
|
||||
// get a tm structure
|
||||
time_t temp = time(NULL);
|
||||
@@ -163,7 +162,7 @@ static void update_minute_1() {
|
||||
//// calculate new positions
|
||||
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);
|
||||
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
|
||||
////// 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);
|
||||
@@ -186,7 +185,6 @@ static void update_minute_1() {
|
||||
s_date_grect_a = s_date_grect_b;
|
||||
s_bt_grect_a = s_bt_grect_b;
|
||||
}
|
||||
first_minute_update = false;
|
||||
|
||||
// DEBUG memory usage layer
|
||||
// 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]);
|
||||
|
||||
// swap colors
|
||||
replace_gbitmap_color(GColorGreen, s_random_color_next, s_butt, NULL);
|
||||
replace_gbitmap_color(s_random_color_current, s_random_color_next, s_head, NULL);
|
||||
replace_gbitmap_color(GColorGreen, s_random_color_next, s_butt);
|
||||
replace_gbitmap_color(s_random_color_current, s_random_color_next, s_head);
|
||||
s_random_color_current = s_random_color_next;
|
||||
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);
|
||||
}
|
||||
|
||||
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) {
|
||||
// destroy and reassign
|
||||
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]);
|
||||
|
||||
// swap colors
|
||||
replace_gbitmap_color(GColorGreen, s_random_color_next, s_head, NULL);
|
||||
replace_gbitmap_color(s_random_color_current, s_random_color_next, s_butt, NULL);
|
||||
replace_gbitmap_color(GColorGreen, s_random_color_next, s_head);
|
||||
replace_gbitmap_color(s_random_color_current, s_random_color_next, s_butt);
|
||||
s_random_color_current = s_random_color_next;
|
||||
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);
|
||||
}
|
||||
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
update_minute_30();
|
||||
} else {
|
||||
update_minute_1();
|
||||
schedule_guy_animation(s_butt_raise_template, s_butt_out_template, s_butt_in_template, update_minute_30_out_handler);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -353,13 +345,13 @@ static void bluetooth_callback(bool connected) {
|
||||
// DEBUG buttons
|
||||
// static void up_single_click_handler(ClickRecognizerRef recognizer, void *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
|
||||
// static void down_single_click_handler(ClickRecognizerRef recognizer, void *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
|
||||
@@ -371,7 +363,13 @@ static void bluetooth_callback(bool connected) {
|
||||
// set up the app on launch (don't put app logic in here);
|
||||
static void init() {
|
||||
// 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
|
||||
s_main_window = window_create();
|
||||
@@ -399,8 +397,8 @@ static void init() {
|
||||
|
||||
// set starting colors
|
||||
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_butt, NULL);
|
||||
replace_gbitmap_color(GColorGreen, s_random_color_current, s_head);
|
||||
replace_gbitmap_color(GColorGreen, s_random_color_current, s_butt);
|
||||
|
||||
// load custom font
|
||||
#if PBL_DISPLAY_WIDTH <= 180
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
|
||||
#include "palette_manip.h"
|
||||
|
||||
#ifdef PBL_COLOR
|
||||
|
||||
int get_num_palette_colors(GBitmap *b){
|
||||
GBitmapFormat format = gbitmap_get_format(b);
|
||||
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
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
//Mark the bitmaplayer dirty
|
||||
if(bml != NULL){
|
||||
layer_mark_dirty(bitmap_layer_get_layer(bml));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
@@ -3,5 +3,5 @@
|
||||
#include <pebble.h>
|
||||
|
||||
#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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||