Compare commits
No commits in common. "531d5bdb6ff2437d504afdeaf7d6a34cb17d54f3" and "d20c12fec6ca9ea2eca2749b1791ee8bbe2b0703" have entirely different histories.
531d5bdb6f
...
d20c12fec6
BIN
assets/screenshots/chalk.png
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
BIN
assets/screenshots/diorite.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
32
flake.lock
generated
@ -113,20 +113,6 @@
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1765270179,
|
||||
"narHash": "sha256-g2a4MhRKu4ymR4xwo+I+auTknXt/+j37Lnf0Mvfl1rE=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "677fbe97984e7af3175b6c121f3c39ee5c8d62c9",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"id": "nixpkgs",
|
||||
"type": "indirect"
|
||||
}
|
||||
},
|
||||
"nixpkgs_2": {
|
||||
"locked": {
|
||||
"lastModified": 1734649271,
|
||||
"narHash": "sha256-4EVBRhOjMDuGtMaofAIqzJbg4Ql7Ai0PSeuVZTHjyKQ=",
|
||||
@ -147,26 +133,24 @@
|
||||
"commit-hooks": "commit-hooks",
|
||||
"flake-compat": "flake-compat_2",
|
||||
"flake-utils": "flake-utils_2",
|
||||
"nixpkgs": "nixpkgs_2"
|
||||
"nixpkgs": "nixpkgs"
|
||||
},
|
||||
"locked": {
|
||||
"dirtyRev": "3fe382c507afd73a2c6a51ece280c3fb567ac53a-dirty",
|
||||
"dirtyShortRev": "3fe382c-dirty",
|
||||
"lastModified": 1764961369,
|
||||
"narHash": "sha256-FPMMcBAoeQgA/W92XTOAmTdDMmhbpNgsuSia7BMOBUs=",
|
||||
"owner": "diomidova",
|
||||
"repo": "pebble.nix",
|
||||
"rev": "3fe382c507afd73a2c6a51ece280c3fb567ac53a",
|
||||
"type": "github"
|
||||
"narHash": "sha256-TE0XcWkJNwMVKonZ8y/c4kVLEp71A+45T5Fys2Jv2uI=",
|
||||
"type": "git",
|
||||
"url": "file:///home/diomidova/Pebble/pebble.nix"
|
||||
},
|
||||
"original": {
|
||||
"owner": "diomidova",
|
||||
"repo": "pebble.nix",
|
||||
"type": "github"
|
||||
"type": "git",
|
||||
"url": "file:///home/diomidova/Pebble/pebble.nix"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"flake-utils": "flake-utils",
|
||||
"nixpkgs": "nixpkgs",
|
||||
"pebble": "pebble"
|
||||
}
|
||||
},
|
||||
|
||||
13
flake.nix
@ -8,19 +8,10 @@
|
||||
};
|
||||
|
||||
outputs =
|
||||
{ pebble, flake-utils, nixpkgs, ... }:
|
||||
flake-utils.lib.eachDefaultSystem (system:
|
||||
let pkgs = import nixpkgs {
|
||||
overlays = [ ];
|
||||
inherit system;
|
||||
};
|
||||
in
|
||||
{
|
||||
{ pebble, flake-utils, ... }:
|
||||
flake-utils.lib.eachDefaultSystem (system: {
|
||||
devShell = pebble.pebbleEnv.${system} {
|
||||
withCoreDevices = true;
|
||||
packages = [
|
||||
pkgs.python312Packages.ipython
|
||||
];
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
7
package-lock.json
generated
@ -10,7 +10,6 @@
|
||||
"license": "GPL-3.0-or-later",
|
||||
"dependencies": {
|
||||
"@rebble/sdk-c-utils": "^1.2.3",
|
||||
"pebble-clay": "^1.0.4",
|
||||
"pebble-cpp": "^0.3.1"
|
||||
}
|
||||
},
|
||||
@ -20,12 +19,6 @@
|
||||
"integrity": "sha512-24b/qtVpviGJyVhvQYTsFojRn30YcVSepeCVbi7AqrNPfitGNC2EqwLLpKZuH1QTnE6UcTgjLrh6yda7p6UvAg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/pebble-clay": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/pebble-clay/-/pebble-clay-1.0.4.tgz",
|
||||
"integrity": "sha512-/rXxmltdW8JyohDzXINdea+d2wnFJVNFiTXfuZsKpySURZSCFMMucX9sZPZvbHnEA4xFINM4iicyhBbvY4ALfw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/pebble-cpp": {
|
||||
"version": "0.3.1",
|
||||
"resolved": "https://registry.npmjs.org/pebble-cpp/-/pebble-cpp-0.3.1.tgz",
|
||||
|
||||
@ -9,7 +9,6 @@
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@rebble/sdk-c-utils": "^1.2.3",
|
||||
"pebble-clay": "^1.0.4",
|
||||
"pebble-cpp": "^0.3.1"
|
||||
},
|
||||
"pebble": {
|
||||
@ -26,11 +25,8 @@
|
||||
"watchface": true
|
||||
},
|
||||
"messageKeys": [
|
||||
"BACKGROUND_COLOR",
|
||||
"DIGIT_COLOR",
|
||||
"FRAME_COLOR"
|
||||
"dummy"
|
||||
],
|
||||
"capabilities": [ "configurable" ],
|
||||
"resources": {
|
||||
"media": [
|
||||
{
|
||||
|
||||
|
Before Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 1.3 KiB |
@ -4,11 +4,10 @@
|
||||
using namespace Pebble;
|
||||
|
||||
// TODO move this to pebble-cpp
|
||||
bool operator==(const GColor& lhs, const GColor& rhs) { return gcolor_equal(lhs, rhs); }
|
||||
bool operator!=(const GColor& lhs, const GColor& rhs) { return !gcolor_equal(lhs, rhs); }
|
||||
bool operator==(const GColor8& lhs, const GColor8& rhs) { return lhs.argb == rhs.argb; }
|
||||
bool operator!=(const GColor8& lhs, const GColor8& rhs) { return lhs.argb != rhs.argb; }
|
||||
|
||||
namespace bitmaps {
|
||||
namespace {
|
||||
namespace {
|
||||
GOwnedBitmap digitsBitmap;
|
||||
GOwnedBitmap digitBitmaps[10];
|
||||
GOwnedBitmap frameBitmap;
|
||||
@ -17,7 +16,7 @@ namespace bitmaps {
|
||||
GColor* frameColor;
|
||||
|
||||
|
||||
int GetPaletteSize(GBitmapBase& bitmap){
|
||||
int get_palette_size(GBitmapBase& bitmap){
|
||||
switch (bitmap.GetFormat()) {
|
||||
case GBitmapFormat1BitPalette: return 2;
|
||||
case GBitmapFormat2BitPalette: return 4;
|
||||
@ -26,14 +25,14 @@ namespace bitmaps {
|
||||
}
|
||||
}
|
||||
|
||||
GColor* FindColor(GBitmapBase& bitmap, GColor color) {
|
||||
GColor* find_color(GBitmapBase& bitmap, GColor color) {
|
||||
GColor* palette = bitmap.GetPalette();
|
||||
if (palette == nullptr) {
|
||||
ERROR("Palette is null");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int paletteSize = GetPaletteSize(bitmap);
|
||||
int paletteSize = get_palette_size(bitmap);
|
||||
if (paletteSize == 0) {
|
||||
ERROR("Expected GBitmapFormat[124]BitPalette, got: %d", digitsBitmap.GetFormat());
|
||||
return nullptr;
|
||||
@ -47,9 +46,9 @@ namespace bitmaps {
|
||||
ERROR("Color 0x%02X not found in the palette", color);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Init() {
|
||||
void init_bitmaps() {
|
||||
digitsBitmap = GOwnedBitmap::CreateWithResource(RESOURCE_ID_DIGITS);
|
||||
auto size = digitsBitmap.GetBounds().size;
|
||||
uint16_t w = size.w / 10;
|
||||
@ -60,11 +59,11 @@ namespace bitmaps {
|
||||
|
||||
frameBitmap = GOwnedBitmap::CreateWithResource(RESOURCE_ID_FRAME);
|
||||
|
||||
digitColor = FindColor(digitsBitmap, GColorBlack);
|
||||
frameColor = FindColor(frameBitmap, GColorBlack);
|
||||
}
|
||||
digitColor = find_color(digitsBitmap, GColorBlack);
|
||||
frameColor = find_color(frameBitmap, GColorBlack);
|
||||
}
|
||||
|
||||
void DeInit() {
|
||||
void deinit_bitmaps() {
|
||||
for (int i = 0; i < 10; i++) {
|
||||
digitBitmaps[i] = GOwnedBitmap();
|
||||
}
|
||||
@ -72,29 +71,28 @@ namespace bitmaps {
|
||||
frameBitmap = GOwnedBitmap();
|
||||
digitColor = nullptr;
|
||||
frameColor = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
GBitmapBase& GetDigitBitmap(uint16_t digit) {return digitBitmaps[digit];}
|
||||
GBitmapBase& GetFrameBitmap() {return frameBitmap;}
|
||||
GBitmapBase& get_digit_bitmap(uint16_t digit) {return digitBitmaps[digit];}
|
||||
GBitmapBase& get_frame_bitmap() {return frameBitmap;}
|
||||
|
||||
|
||||
void SetDigitColor(GColor color) {
|
||||
void set_digit_color(GColor color) {
|
||||
if (digitColor != nullptr) *digitColor = color;
|
||||
}
|
||||
void SetFrameColor(GColor color) {
|
||||
}
|
||||
void set_frame_color(GColor color) {
|
||||
if (frameColor != nullptr) *frameColor = color;
|
||||
}
|
||||
}
|
||||
|
||||
void PrintPalette(GBitmapBase& bitmap) {
|
||||
int paletteSize = GetPaletteSize(bitmap);
|
||||
void print_palette(GBitmapBase& bitmap) {
|
||||
int paletteSize = get_palette_size(bitmap);
|
||||
if (paletteSize == 0) {
|
||||
ERROR("Expected GBitmapFormat[124]BitPalette, got: %d", digitsBitmap.GetFormat());
|
||||
return;
|
||||
}
|
||||
DEBUG("%d colors:", paletteSize);
|
||||
GColor* palette = bitmap.GetPalette();
|
||||
GColor8* palette = bitmap.GetPalette();
|
||||
for (int i = 0; i < paletteSize; i++) {
|
||||
DEBUG("* 0x%02X = argb(%d,%d,%d,%d)", palette[i].argb, palette[i].a, palette[i].r, palette[i].g, palette[i].b);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2,12 +2,10 @@
|
||||
|
||||
#include <pebble-cpp/pebble.hpp>
|
||||
|
||||
namespace bitmaps {
|
||||
void Init();
|
||||
void DeInit();
|
||||
Pebble::GBitmapBase& GetDigitBitmap(uint16_t digit);
|
||||
Pebble::GBitmapBase& GetFrameBitmap();
|
||||
void SetDigitColor(GColor color);
|
||||
void SetFrameColor(GColor color);
|
||||
void PrintPalette(Pebble::GBitmapBase& bitmap);
|
||||
}
|
||||
void init_bitmaps();
|
||||
void deinit_bitmaps();
|
||||
Pebble::GBitmapBase& get_digit_bitmap(uint16_t digit);
|
||||
Pebble::GBitmapBase& get_frame_bitmap();
|
||||
void set_digit_color(GColor color);
|
||||
void set_frame_color(GColor color);
|
||||
void print_palette(Pebble::GBitmapBase& bitmap);
|
||||
@ -3,18 +3,18 @@
|
||||
#include "pebble_bitmap_layer.hpp"
|
||||
#include "digit_layer.hpp"
|
||||
#include "bitmaps.hpp"
|
||||
#include "settings.hpp"
|
||||
|
||||
using namespace Pebble;
|
||||
|
||||
// For debugging
|
||||
const bool USE_SECONDS_INSTEAD_OF_MINUTES = false;
|
||||
#undef DEBUG
|
||||
|
||||
class MainWindow : public AppWindow, public TickTimer {
|
||||
DigitLayer digitLayers[4];
|
||||
Pebble::BitmapLayer frameLayer;
|
||||
protected:
|
||||
void OnLoad() {
|
||||
init_bitmaps();
|
||||
|
||||
auto bounds = this->GetBounds();
|
||||
int16_t w2 = bounds.size.w / 2;
|
||||
int16_t h = bounds.size.h;
|
||||
@ -23,35 +23,50 @@ protected:
|
||||
digitLayers[1].SetFrame(Pebble::GRect(w2-36, 0, 32, h));
|
||||
digitLayers[2].SetFrame(Pebble::GRect(w2+4, 0, 32, h));
|
||||
digitLayers[3].SetFrame(Pebble::GRect(w2+36, 0, 32, h));
|
||||
digitLayers[0].SetDigitCount(clock_is_24h_style() ? 3 : 2);
|
||||
digitLayers[1].SetDigitCount(clock_is_24h_style() ? 24 : 12);
|
||||
digitLayers[2].SetDigitCount(6);
|
||||
digitLayers[3].SetDigitCount(10);
|
||||
for (size_t i = 0; i < 4; i++) {
|
||||
this->digitLayers[i].UpdateLayout();
|
||||
this->AddChild(this->digitLayers[i]);
|
||||
}
|
||||
|
||||
this->frameLayer.SetBitmap(bitmaps::GetFrameBitmap());
|
||||
// print_palette(get_digit_bitmap(0));
|
||||
// print_palette(get_frame_bitmap());
|
||||
// set_frame_color(GColorPictonBlue);
|
||||
// print_palette(get_digit_bitmap(0));
|
||||
// print_palette(get_frame_bitmap());
|
||||
this->frameLayer.SetBitmap(get_frame_bitmap());
|
||||
this->frameLayer.SetFrame(bounds);
|
||||
this->frameLayer.SetCompositingMode(GCompOpSet);
|
||||
this->AddChild(this->frameLayer);
|
||||
|
||||
// MINUTE_UNIT doesn't seem to be called if only the hours change
|
||||
// This can happen when time zone changes or in tests
|
||||
// So we also update time on HOUR_UNIT
|
||||
TickTimerStart(HOUR_UNIT);
|
||||
TickTimerStart(USE_SECONDS_INSTEAD_OF_MINUTES ? SECOND_UNIT : MINUTE_UNIT);
|
||||
#ifdef DEBUG
|
||||
TickTimerStart(SECOND_UNIT);
|
||||
#else
|
||||
TickTimerStart(MINUTE_UNIT);
|
||||
#endif
|
||||
UpdateTime(false);
|
||||
|
||||
this->SetBackgroundColor(GColorBlack);
|
||||
set_digit_color(GColorVividCerulean);
|
||||
set_frame_color(GColorPictonBlue);
|
||||
}
|
||||
|
||||
void OnUnload() {
|
||||
deinit_bitmaps();
|
||||
}
|
||||
|
||||
void UpdateTime(bool animate=true) {
|
||||
DateTime dateTime = DateTime::Now();
|
||||
int hour = dateTime.GetHour();
|
||||
int minute = USE_SECONDS_INSTEAD_OF_MINUTES ? dateTime.GetSecond() : dateTime.GetMinute();
|
||||
bool is_24h = clock_is_24h_style();
|
||||
if (!is_24h) hour %= 12;
|
||||
|
||||
digitLayers[0].SetDigitCount(is_24h ? 3 : 2);
|
||||
digitLayers[1].SetDigitCount(is_24h ? 24 : 12);
|
||||
digitLayers[2].SetDigitCount(6);
|
||||
digitLayers[3].SetDigitCount(10);
|
||||
DateTime date = DateTime::Now();
|
||||
int hour = date.GetHour();
|
||||
#ifdef DEBUG
|
||||
int minute = date.GetSecond();
|
||||
#else
|
||||
int minute = date.GetMinute();
|
||||
#endif
|
||||
if (!clock_is_24h_style()) hour %= 12;
|
||||
this->digitLayers[0].RotateTo(hour / 10, animate);
|
||||
this->digitLayers[1].RotateTo(hour, animate);
|
||||
this->digitLayers[2].RotateTo(minute / 10, animate);
|
||||
@ -63,34 +78,9 @@ protected:
|
||||
}
|
||||
};
|
||||
|
||||
namespace {
|
||||
void ApplySettings() {
|
||||
Pebble::Window window = window_stack_get_top_window();
|
||||
window.SetBackgroundColor(settings::GetBackgroundColor());
|
||||
window.MarkDirty();
|
||||
|
||||
bitmaps::SetDigitColor(settings::GetDigitColor());
|
||||
bitmaps::SetFrameColor(settings::GetFrameColor());
|
||||
}
|
||||
|
||||
void HandleMsg(DictionaryIterator *iter, void *context) {
|
||||
settings::HandleMsg(iter);
|
||||
ApplySettings();
|
||||
}
|
||||
}
|
||||
|
||||
MAIN {
|
||||
bitmaps::Init();
|
||||
settings::Load();
|
||||
app_message_register_inbox_received(HandleMsg);
|
||||
app_message_open(64, 64); // inbox, outbox
|
||||
|
||||
MainWindow window;
|
||||
window.StackPush(true);
|
||||
ApplySettings();
|
||||
|
||||
app_event_loop();
|
||||
|
||||
bitmaps::DeInit();
|
||||
return 0;
|
||||
}
|
||||
@ -1,12 +1,12 @@
|
||||
#include "digit_layer.hpp"
|
||||
#include <@rebble/sdk-c-utils/macros.h>
|
||||
#include "bitmaps.hpp"
|
||||
#include <@rebble/sdk-c-utils/macros.h>
|
||||
|
||||
using namespace Pebble;
|
||||
|
||||
namespace {
|
||||
void draw_digit(const Pebble::GContext& ctx, const Pebble::GRect& bounds, const uint16_t digit) {
|
||||
Pebble::GBitmapBase& bitmap = bitmaps::GetDigitBitmap(digit);
|
||||
Pebble::GBitmapBase& bitmap = get_digit_bitmap(digit);
|
||||
auto rect = bitmap.GetBounds();
|
||||
rect.Align(bounds, GAlignCenter, false);
|
||||
ctx.DrawBitmapInRect(bitmap, rect);
|
||||
@ -15,13 +15,11 @@ namespace {
|
||||
|
||||
void DigitLayerInner::OnUpdate(const Pebble::GContext& ctx) {
|
||||
auto bounds = this->GetBounds();
|
||||
// LOG("Digit layer update. %d digits", this->digitCount);
|
||||
|
||||
ctx.SetCompositingMode(GCompOpSet);
|
||||
for (int i = 0; i <= this->digitCount + 2 * this->extraDigits; i++) {
|
||||
Pebble::GRect rect(0, i * this->digitHeight, bounds.size.w, this->digitHeight);
|
||||
uint16_t digit = ((i - this->extraDigits) % this->digitCount + this->digitCount) % this->digitCount % 10;
|
||||
// LOG("Digit %d -> %d", i - this->extraDigits, digit);
|
||||
draw_digit(ctx, rect, digit);
|
||||
}
|
||||
}
|
||||
@ -48,7 +46,6 @@ int16_t DigitLayer::ComputeOffset(uint16_t digit) {
|
||||
}
|
||||
|
||||
void DigitLayer::RotateTo(uint16_t digit, bool animate) {
|
||||
// LOG("Digit %d, out of %d", digit, innerLayer.digitCount);
|
||||
digit %= innerLayer.digitCount;
|
||||
auto currentOffset = this->scrollLayer.GetContentOffset();
|
||||
if (digit == 0 && currentOffset.y <= ComputeOffset(this->innerLayer.digitCount / 2)) {
|
||||
|
||||
@ -9,6 +9,11 @@ class DigitLayerInner : public Pebble::CustomLayer {
|
||||
|
||||
DigitLayerInner() : Pebble::CustomLayer(Pebble::GRect()) {}
|
||||
|
||||
void SetDigitCount(uint16_t cnt = 10) {
|
||||
this->digitCount = cnt;
|
||||
this->MarkDirty();
|
||||
}
|
||||
|
||||
static void DrawDigit(const Pebble::GContext& ctx, const Pebble::GRect& bounds, const uint16_t digit);
|
||||
|
||||
friend class DigitLayer;
|
||||
@ -24,10 +29,8 @@ public:
|
||||
DigitLayer();
|
||||
|
||||
void SetDigitCount(uint16_t cnt = 10) {
|
||||
if (this->innerLayer.digitCount != cnt) {
|
||||
this->innerLayer.digitCount = cnt;
|
||||
this->UpdateLayout();
|
||||
}
|
||||
this->innerLayer.SetDigitCount(cnt);
|
||||
this->MarkDirty();
|
||||
}
|
||||
void UpdateLayout();
|
||||
void RotateTo(uint16_t digit, bool animate=true);
|
||||
|
||||
@ -1,43 +0,0 @@
|
||||
#include "settings.hpp"
|
||||
|
||||
// Built-in GColorFromRGBA doesn't seem to work in C++11 :(
|
||||
#undef GColorFromRGBA
|
||||
GColor8 GColorFromRGBA(uint8_t red, uint8_t green, uint8_t blue, uint8_t alpha) {
|
||||
GColor8 res;
|
||||
res.a = alpha >> 6;
|
||||
res.r = red >> 6;
|
||||
res.g = green >> 6;
|
||||
res.b = blue >> 6;
|
||||
return res;
|
||||
}
|
||||
|
||||
namespace settings {
|
||||
namespace {
|
||||
GColor backgroundColor = GColorWhite;
|
||||
GColor digitColor = GColorBlack;
|
||||
GColor frameColor = GColorBlack;
|
||||
}
|
||||
|
||||
void Save() {
|
||||
persist_write_data(MESSAGE_KEY_BACKGROUND_COLOR, &backgroundColor, sizeof(GColor));
|
||||
persist_write_data(MESSAGE_KEY_DIGIT_COLOR, &digitColor, sizeof(GColor));
|
||||
persist_write_data(MESSAGE_KEY_FRAME_COLOR, &frameColor, sizeof(GColor));
|
||||
}
|
||||
|
||||
void Load() {
|
||||
persist_read_data(MESSAGE_KEY_BACKGROUND_COLOR, &backgroundColor, sizeof(GColor));
|
||||
persist_read_data(MESSAGE_KEY_DIGIT_COLOR, &digitColor, sizeof(GColor));
|
||||
persist_read_data(MESSAGE_KEY_FRAME_COLOR, &frameColor, sizeof(GColor));
|
||||
}
|
||||
|
||||
void HandleMsg(DictionaryIterator *iter) {
|
||||
if (auto tuple = dict_find(iter, MESSAGE_KEY_BACKGROUND_COLOR)) backgroundColor = GColorFromHEX(tuple->value->int32);
|
||||
if (auto tuple = dict_find(iter, MESSAGE_KEY_DIGIT_COLOR)) digitColor = GColorFromHEX(tuple->value->int32);
|
||||
if (auto tuple = dict_find(iter, MESSAGE_KEY_FRAME_COLOR)) frameColor = GColorFromHEX(tuple->value->int32);
|
||||
Save();
|
||||
}
|
||||
|
||||
GColor GetBackgroundColor() {return backgroundColor;}
|
||||
GColor GetDigitColor() {return digitColor;}
|
||||
GColor GetFrameColor() {return frameColor;}
|
||||
}
|
||||
@ -1,12 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <pebble-cpp/pebble.hpp>
|
||||
|
||||
namespace settings {
|
||||
void Save();
|
||||
void Load();
|
||||
void HandleMsg(DictionaryIterator *iter);
|
||||
GColor GetBackgroundColor();
|
||||
GColor GetDigitColor();
|
||||
GColor GetFrameColor();
|
||||
}
|
||||
@ -1,36 +0,0 @@
|
||||
module.exports = [
|
||||
{
|
||||
type: "section",
|
||||
items: [
|
||||
{
|
||||
type: "heading",
|
||||
defaultValue: "Colors",
|
||||
},
|
||||
{
|
||||
label: "Background Color",
|
||||
messageKey: "BACKGROUND_COLOR",
|
||||
type: "color",
|
||||
defaultValue: "#FFFFFF",
|
||||
allowGray: true,
|
||||
},
|
||||
{
|
||||
label: "Digit Color",
|
||||
messageKey: "DIGIT_COLOR",
|
||||
type: "color",
|
||||
defaultValue: "#000000",
|
||||
allowGray: true,
|
||||
},
|
||||
{
|
||||
label: "Frame Color",
|
||||
messageKey: "FRAME_COLOR",
|
||||
type: "color",
|
||||
defaultValue: "#00000",
|
||||
allowGray: true,
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
type: "submit",
|
||||
defaultValue: "Save Settings",
|
||||
},
|
||||
];
|
||||
@ -1,3 +0,0 @@
|
||||
var Clay = require('pebble-clay');
|
||||
var clayConfig = require('./config');
|
||||
var clay = new Clay(clayConfig);
|
||||
@ -1,141 +0,0 @@
|
||||
# A simple script to automatically take app screenshots
|
||||
# Hopefully some day it'll grow into an actual testing framework
|
||||
|
||||
from uuid import UUID
|
||||
from datetime import datetime, timezone
|
||||
from time import sleep
|
||||
import os
|
||||
import png
|
||||
import json
|
||||
|
||||
from pebble_tool.util.logs import PebbleLogPrinter
|
||||
from pebble_tool.sdk.emulator import ManagedEmulatorTransport
|
||||
from pebble_tool.commands.emucontrol import send_data_to_qemu
|
||||
from libpebble2.communication import PebbleConnection
|
||||
from libpebble2.communication.transports.qemu.protocol import QemuButton, QemuTimeFormat
|
||||
from libpebble2.services.install import AppInstaller
|
||||
from libpebble2.services.appmessage import AppMessageService, Uint32
|
||||
from libpebble2.services.screenshot import Screenshot
|
||||
from libpebble2.protocol.system import TimeMessage, SetUTC
|
||||
|
||||
package_json = None
|
||||
with open('package.json', 'r') as file:
|
||||
package_json = json.load(file)
|
||||
|
||||
app_uuid = UUID(package_json['pebble']['uuid'])
|
||||
appname = package_json['name']
|
||||
target_platforms = package_json['pebble']['targetPlatforms']
|
||||
msg_keys = {
|
||||
key: 10000 + i
|
||||
for i, key in enumerate(package_json['pebble']['messageKeys'])
|
||||
}
|
||||
|
||||
def emulator(platform):
|
||||
print(f'Launching `{platform}` emulator')
|
||||
pebble = PebbleConnection(ManagedEmulatorTransport(platform))
|
||||
pebble.connect()
|
||||
pebble.run_async()
|
||||
print(f'Installing `{appname}.pbw`')
|
||||
AppInstaller(pebble, f'./build/{appname}.pbw').install()
|
||||
sleep(1)
|
||||
return pebble
|
||||
|
||||
def for_all_platforms(fn, logs=True):
|
||||
for platform in target_platforms:
|
||||
pebble = emulator(platform)
|
||||
if logs:
|
||||
logger = PebbleLogPrinter(pebble)
|
||||
fn(pebble)
|
||||
logger.stop()
|
||||
else:
|
||||
fn(pebble)
|
||||
|
||||
|
||||
def is_color(pebble):
|
||||
return pebble.watch_platform in ['basalt', 'chalk', 'emery']
|
||||
|
||||
def send_app_message(pebble, msg):
|
||||
ams = AppMessageService(pebble)
|
||||
tid = ams.send_message(app_uuid, msg)
|
||||
|
||||
# unfortunately the following doesn't work
|
||||
# wait_for_event assumes only one value is returned
|
||||
# but ack returns 2 :(
|
||||
|
||||
# tid2, uuid = ams.wait_for_event('ack')
|
||||
# assert uuid == app_uuid
|
||||
# assert tid2 == tid
|
||||
|
||||
def set_time(pebble, hour, minute, year=2013, month=1, day=23, tzinfo=timezone.utc):
|
||||
dt = int(datetime(year, month, day, hour, minute, tzinfo=tzinfo).timestamp())
|
||||
pebble.send_packet(TimeMessage(message=SetUTC(unix_time=dt, utc_offset=0, tz_name="UTC")))
|
||||
|
||||
def press_buttons(pebble, buttons, duration=0.1):
|
||||
send_data_to_qemu(pebble.transport, QemuButton(state=buttons))
|
||||
sleep(duration)
|
||||
send_data_to_qemu(pebble.transport, QemuButton(state=0))
|
||||
sleep(0.1)
|
||||
|
||||
def press_back(pebble, duration=0.1):
|
||||
press_buttons(pebble, QemuButton.Button.Back, duration)
|
||||
def press_up(pebble, duration=0.1):
|
||||
press_buttons(pebble, QemuButton.Button.Up, duration)
|
||||
def press_select(pebble, duration=0.1):
|
||||
press_buttons(pebble, QemuButton.Button.Select, duration)
|
||||
def press_down(pebble, duration=0.1):
|
||||
press_buttons(pebble, QemuButton.Button.Down, duration)
|
||||
|
||||
def set_24_hour_mode(pebble, is_24_hour=True):
|
||||
send_data_to_qemu(pebble.transport, QemuTimeFormat(is_24_hour=is_24_hour))
|
||||
|
||||
def screenshot(pebble, name):
|
||||
filename = f'{pebble.watch_platform}/{name}.png'
|
||||
path = './screenshots/' + filename
|
||||
print(f'Taking screenshot `{filename}`')
|
||||
os.makedirs(os.path.dirname(path), exist_ok=True)
|
||||
image = Screenshot(pebble).grab_image()
|
||||
png.from_array(image, 'RGB').save(path)
|
||||
|
||||
# ============================================================
|
||||
# Code above this line is not specific to the watchface and
|
||||
# should probably be eventually split off into its own library
|
||||
# ============================================================
|
||||
|
||||
def set_colors(pebble, background, digits, frame):
|
||||
def wrap_color(color):
|
||||
if isinstance(color, int):
|
||||
color = hex(color)
|
||||
color = color.removeprefix('0x').removeprefix('#')
|
||||
if len(color) == 3:
|
||||
r, g, b = color
|
||||
color = r + r + g + g + b + b
|
||||
return Uint32(int(color, 16))
|
||||
send_app_message(pebble, {
|
||||
msg_keys["BACKGROUND_COLOR"]: wrap_color(background),
|
||||
msg_keys["DIGIT_COLOR"]: wrap_color(digits),
|
||||
msg_keys["FRAME_COLOR"]: wrap_color(frame),
|
||||
})
|
||||
|
||||
def run(pebble):
|
||||
set_24_hour_mode(pebble, True)
|
||||
set_time(pebble, 12, 34)
|
||||
set_colors(pebble, 'FFF', '000', '000')
|
||||
sleep(1.25)
|
||||
screenshot(pebble, '01_white-black-black')
|
||||
|
||||
set_24_hour_mode(pebble, False)
|
||||
set_time(pebble, 16, 59)
|
||||
set_colors(pebble, '000', 'FFF', 'AAA')
|
||||
sleep(1.25)
|
||||
screenshot(pebble, '02_black-white-gray')
|
||||
|
||||
if is_color(pebble):
|
||||
set_24_hour_mode(pebble, True)
|
||||
set_time(pebble, 4, 20)
|
||||
set_colors(pebble, 'FF5', '00F', '00A')
|
||||
sleep(1.25)
|
||||
screenshot(pebble, '03_yellow-blue-blue')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
for_all_platforms(run)
|
||||