From 643487f4b3e539aa4fba0194c9de4df6bf4ff616 Mon Sep 17 00:00:00 2001 From: Jenny Diomidova Date: Mon, 8 Dec 2025 12:21:57 +0100 Subject: [PATCH] initial commit --- .gitignore | 12 +++ README.md | 0 assets/screenshots/chalk.png | Bin 0 -> 2061 bytes assets/screenshots/diorite.png | Bin 0 -> 1427 bytes flake.lock | 190 +++++++++++++++++++++++++++++++++ flake.nix | 17 +++ package-lock.json | 28 +++++ package.json | 50 +++++++++ resources/digits.png | Bin 0 -> 659 bytes resources/frame.png | Bin 0 -> 430 bytes resources/icon.png | Bin 0 -> 285 bytes src/c/cryptic_clock.cpp | 72 +++++++++++++ src/c/digit_layer.cpp | 73 +++++++++++++ src/c/digit_layer.hpp | 40 +++++++ src/c/pebble_bitmap_layer.hpp | 34 ++++++ wscript | 67 ++++++++++++ 16 files changed, 583 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 assets/screenshots/chalk.png create mode 100644 assets/screenshots/diorite.png create mode 100644 flake.lock create mode 100644 flake.nix create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 resources/digits.png create mode 100644 resources/frame.png create mode 100644 resources/icon.png create mode 100644 src/c/cryptic_clock.cpp create mode 100644 src/c/digit_layer.cpp create mode 100644 src/c/digit_layer.hpp create mode 100644 src/c/pebble_bitmap_layer.hpp create mode 100644 wscript diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..af03d49 --- /dev/null +++ b/.gitignore @@ -0,0 +1,12 @@ + +__pycache__/ +*.py[oc] +build/ +dist/ +wheels/ +*.egg-info + +node_modules + +.venv +.lock-waf_*_build diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/assets/screenshots/chalk.png b/assets/screenshots/chalk.png new file mode 100644 index 0000000000000000000000000000000000000000..12c2228f5d132b923c49b7c3259092e9bda0bab3 GIT binary patch literal 2061 zcmZ`)c~lbU7N^WTwM|?oNk=nHa$(eGPh4<8)C?8Va!GP|JX7f@reSU=PkZK;V~Hq8 znxUB?sM9hoSyq-Lqfjmh`3hvr#8Ht^Lk4HgJMaDT{`kJ{-2408``vrbJ?D1~6@<{y zGS&hB06IuN?-1qNwS6?ylsAm6<^TYwbCKSj=&bs$ItS* zNLm&E<`{na4ok0eu^a=xQWf0)K5ADaKa87guY(F8{ELv+0!Y$ip(MOL!Eavp_TsAZ z&$e09C9%4}+Gu@gbReMHW(FmA^OFa}HenCPXQ&)N8p)f zYF^wnqu;8C&3~T#PMt?C^RR$aC(sp-We;o9C%r|M*689_Otp3F0}-MWMjm?m?ZCA* z2CLok#8h%_^MMtX3rX)CU5%veWk&{E4Sh=vnk&p08;qE+Vy#z&jEt(;gWGG=M1w)K z?1~@G=Q`2XV|yPS1zsAod6s0A2=!pUAo~Kk+dW+;{L5=XMiL6@$Y#+;?v`$x3G+;7 z=Gix`kUL9? z|Aqw&G!*8Qh9i7xH`TuqpQE>Azf?%u zu&eELk8{_j`E6<~c^!o#N`2nfBcb2tgM&k`OVDwqHT1~HZ1qwJ6K?5hKnZKG#hB=2 zZP;qhz|M^Y>LO<$_)k}m&=n+dD}nZ8E})(qaCX`DTR*<)z@q8utg`dE-Jca07Ppkr9wkPE0K#QsPJ;4DCIGbCSWi z5Ft8JuO&y$F*!OO8geU37DtD{`J(hjh%;>P`)yW?Z%#%P6P>~_GuED*V&79#4t`8KsV$yX0LV|G-l(oF?v0JRb>UGyAGR}jAq`@{d4k9< zi|!EaB*-)v_R@kj)!Y0?2sqpAqHc^x@?tfvtz5t19E^J2Ei32{jWw4T8YbdhX1m*HNZ%PVJBG6uDh)oR2+9;<>QaBWRi6k@@sG zMUTAxEh02V%tJs4jcI}&>H_Y&t~~_d?4*FTa%p;WQHHg_hb*S9pao5x9OR=X?9iNw z7jR4AqSGlU%vigjHX2sky&V-H{`_rWYhgQI9IdoByao!=p7F+hCf69TIyq%UzCT4a zoN@%asq;DTNb$gx-wtMZX{XRsR%X*b>Oi`Px+BG-1lfh-gpbfW8 z886A%U0LI5&YI*NFE#gjAc6LB@y8N@FFztoG(O|Y;8eTamGI@!_@D&RUP%l^1ve3e z|!Q ze>UjaRKby)Z@Yt$dyyMX*@Io@YzJD&>j@~(*q;o0MjnH2!g|5*mb z4yVjVEvlu- z-111&Rm`83?G49&q`J{9;=zHi{_H-#cg>`OsDKi#2=1>P3Oi?hVJy(3#KJo%-SiHz zm7-*0OsL})?eCt3d^R#%H?)HSxF0^@HIHRHtQ{Lev%9PHT>}t!%|HrU06E>)##pPi z{$hQvG#0A6YwOJQ>ou4K%V@4fr9pv_PSGNKE!VHELuJCK9z(;#ONyKSzI%Y*xLaH2 zyL5MRm<;e$Z;-*UW3G9PE${wl5`JqL-`P~axo)K4uvUHCY~PZb03sBoQRS1Vovd(` zUMhOs;nmp>{-)XTg=@`89S!Xu`9?v^R1$47#noq-A6RJwNczPCViQ7wRiQ@>hKk1nHCy9VyUum2m~|Awz$_m>)ERO)3K?rQtV zlpDZg8nQCu7nQ7G;0Yt932evlX}Bf)-EHSaV&uH}L#L?cvttlP!}(uv!q|17v4&Jk z9T4rcSvCD|EblN#y>kS_kZk-+ugLl9{ARAr{4>EnHz*=dx5iAcV02qEow+iH9b6{b zC&wVY2)6mbT>ahNrs||g!kFD1xQiCLCpyR QrOX46K0)3c@Yt)r1IxAtNdN!< literal 0 HcmV?d00001 diff --git a/assets/screenshots/diorite.png b/assets/screenshots/diorite.png new file mode 100644 index 0000000000000000000000000000000000000000..d477ad4c0ab2e3715844007e2ed0ee4d35446da0 GIT binary patch literal 1427 zcmeAS@N?(olHy`uVBq!ia0vp^6M%RH2OE&&&rh#lU|@~%ba4!+nDchl&Ab>z0oLdL z>u+7u5DIz}*>G7q#M7qmrQH`@_jwg^_6$t@Zx}8}voLW&k+jl)3aS` zip_?b;+cOZDOYP&d^KM=#a4y8auMq)1Nq6fiW;9NMxLCk7|{Cj#MjmAA-|Ocm^}Xq zax^aa$I;?Y)Ha*-*OZG(w#6(s(2!PiN9V%oIccR&nOxbnoXTaYEL`j0uT!KnA?m>g z##-jaD@^VDFQhq^h*{J)+;>pedf*}RFE*x?taI3|q@Dl@7f1vwcT}DbrQoq(mHx79 zzs2N6g`Q(=%|T?A4b?-^KRVa zys=7`CCQV!G2)Vw!n7@CSfh^a-dFnn)z3x0`=6IFN4K1pI+q3H8vW`|4?7&y4&>$l zxto6V|Gl|@Bg8m9?Egl4X_3aPXQyA;e@kyx3OHL6`SASm?uWN3e&a!joNU6Q{=;l=G zuxT$U8rPh!wvW$!`%M1#J@HU>nc925e`aNy|G)8l-wLY-FFxNpA0EB?=ka~tU9U2_ z&;MSx@7Are_TTpEui~pv~8R7chaId zk3FALzsR3o)3#Nr=$m)fFU8JN;&X`cFnoL*EZJ!jnZvSOq zf_ff(QtSHt%r1wmpVy{Lef{3#tDN6g@es?~Sz$Z>`31j!ct;^XB$cIW$zq34Pr(=e zk6jEAfAPO(s;98VWIm=+#zp}TCMbTC(ReR*NwY(&ifz~e|LH6B0+!dmyv337bCWe! zh~jm>u$}uH-9AsRZFUG0nks(caq4}gfEE{ykPob(=RxjyFDn2viL1kbiPTbp^;4( Qu&`zDboFyt=akR{0P{Da=l}o! literal 0 HcmV?d00001 diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..d02e5fb --- /dev/null +++ b/flake.lock @@ -0,0 +1,190 @@ +{ + "nodes": { + "commit-hooks": { + "inputs": { + "flake-compat": "flake-compat", + "gitignore": "gitignore", + "nixpkgs": [ + "pebble", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1737465171, + "narHash": "sha256-R10v2hoJRLq8jcL4syVFag7nIGE7m13qO48wRIukWNg=", + "owner": "cachix", + "repo": "git-hooks.nix", + "rev": "9364dc02281ce2d37a1f55b6e51f7c0f65a75f17", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "git-hooks.nix", + "type": "github" + } + }, + "flake-compat": { + "flake": false, + "locked": { + "lastModified": 1696426674, + "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "flake-compat_2": { + "locked": { + "lastModified": 1733328505, + "narHash": "sha256-NeCCThCEP3eCl2l/+27kNNK7QrwZB1IJCrXfrbv5oqU=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "ff81ac966bb2cae68946d5ed5fc4994f96d0ffec", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "flake-utils_2": { + "inputs": { + "systems": "systems_2" + }, + "locked": { + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "gitignore": { + "inputs": { + "nixpkgs": [ + "pebble", + "commit-hooks", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1709087332, + "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=", + "owner": "hercules-ci", + "repo": "gitignore.nix", + "rev": "637db329424fd7e46cf4185293b9cc8c88c95394", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "gitignore.nix", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1734649271, + "narHash": "sha256-4EVBRhOjMDuGtMaofAIqzJbg4Ql7Ai0PSeuVZTHjyKQ=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "d70bd19e0a38ad4790d3913bf08fcbfc9eeca507", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "pebble": { + "inputs": { + "commit-hooks": "commit-hooks", + "flake-compat": "flake-compat_2", + "flake-utils": "flake-utils_2", + "nixpkgs": "nixpkgs" + }, + "locked": { + "dirtyRev": "3fe382c507afd73a2c6a51ece280c3fb567ac53a-dirty", + "dirtyShortRev": "3fe382c-dirty", + "lastModified": 1764961369, + "narHash": "sha256-TE0XcWkJNwMVKonZ8y/c4kVLEp71A+45T5Fys2Jv2uI=", + "type": "git", + "url": "file:///home/diomidova/Pebble/pebble.nix" + }, + "original": { + "type": "git", + "url": "file:///home/diomidova/Pebble/pebble.nix" + } + }, + "root": { + "inputs": { + "flake-utils": "flake-utils", + "pebble": "pebble" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, + "systems_2": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..df69be9 --- /dev/null +++ b/flake.nix @@ -0,0 +1,17 @@ +{ + description = "Cryptic Time"; + + inputs = { + # pebble.url = "github:pebble-dev/pebble.nix"; + pebble.url = "github:diomidov/pebble.nix"; + flake-utils.url = "github:numtide/flake-utils"; + }; + + outputs = + { pebble, flake-utils, ... }: + flake-utils.lib.eachDefaultSystem (system: { + devShell = pebble.pebbleEnv.${system} { + withCoreDevices = true; + }; + }); +} diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..ded4275 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,28 @@ +{ + "name": "cryptic-clock", + "version": "1.1", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "cryptic-clock", + "version": "1.1", + "dependencies": { + "@smallstoneapps/utils": "^1.3.0", + "pebble-cpp": "^0.3.1" + } + }, + "node_modules/@smallstoneapps/utils": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@smallstoneapps/utils/-/utils-1.3.0.tgz", + "integrity": "sha512-2XSH7umjEtXqdBTil8yhdLRjw259ZgUhn/c6f/4vW5RlM/jo4J+Hoygc+tr4z8Wv5ymnFvxfNq2MpqTqNM+TTg==", + "license": "MIT" + }, + "node_modules/pebble-cpp": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/pebble-cpp/-/pebble-cpp-0.3.1.tgz", + "integrity": "sha512-lR6/icexx5ol4JfowONUflfKofR8wl6fQB3U1b4OwH1S9Iei12UNv9s7X+gSDSsOoOLSl1fPyf6CGbooH7rxbg==", + "license": "Apache-2.0" + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..d34cc0f --- /dev/null +++ b/package.json @@ -0,0 +1,50 @@ +{ + "name": "cryptic-clock", + "author": "jennyaa", + "version": "1.1", + "keywords": [ + "pebble-app" + ], + "private": true, + "dependencies": { + "@smallstoneapps/utils": "^1.3.0", + "pebble-cpp": "^0.3.1" + }, + "pebble": { + "displayName": "Cryptic Time", + "uuid": "53d1d465-fafe-4e7a-8b4f-44382b32e384", + "sdkVersion": "3", + "enableMultiJS": true, + "targetPlatforms": [ + "basalt", + "chalk", + "diorite" + ], + "watchapp": { + "watchface": true + }, + "messageKeys": [ + "dummy" + ], + "resources": { + "media": [ + { + "type": "bitmap", + "name": "DIGITS", + "file": "digits.png" + }, + { + "type": "bitmap", + "name": "FRAME", + "file": "frame.png" + }, + { + "type": "bitmap", + "name": "MENU_ICON", + "file": "icon.png", + "menuIcon": true + } + ] + } + } +} diff --git a/resources/digits.png b/resources/digits.png new file mode 100644 index 0000000000000000000000000000000000000000..7e2a67f28bfa1e882e5abb468d665eb4b9662cb6 GIT binary patch literal 659 zcmV;E0&M+>P)z@;j|==^1poj5AY({UO#lFTCIA3{ga82g0001h=l}q9FaQARU;qF* zm;eA5aGbhPJOBUy32;bRa{vG?BLDy{BLR4&KXw2B00(qQO+^Rk3*Z=?l z=}AOERCwC$-OF;rAPfailga=8GTnBvNFT8c(miL_=}Z7gs3djiaR7uG8_#VPop)!m zL0!T2g*t7rvdvF^DC5y!qXD#OfB+hxw^wCjE3F~(sAbXRh@}An8X$lMSf8tu#9#c0J^?BM@fUx*1qP_p t06h*N=HgF$;HQEr`_@VP#UFs3eE|lCYjxH=2g?8e002ovPDHLkV1nH^AM*eJ literal 0 HcmV?d00001 diff --git a/resources/frame.png b/resources/frame.png new file mode 100644 index 0000000000000000000000000000000000000000..c95a22c37406270dcf6c949b4fa3b8d8ea706fa2 GIT binary patch literal 430 zcmeAS@N?(olHy`uVBq!ia0vp^JwR;2!3HEFA}^=`DaPU;cPGZ1Cw1z99F}xPUq=Rp zjs4tz5?O(Kg=CK)Uj~LMpu&b028Lfip@tU>45bDP46hOx7_4S6Fo+k-*%fF5l;AAz zh%9Dc&{GCs#)_r(Wk5mp5>H=O_Ln?t;#yW~xjsbz?PKwDaSW-r_4e+;u4V%fhrszi z{_mgj?v~TUUn;*A&D%WVXi31y7^fuGq(BC*JHfrpeK@{Ea{HEjtmSN z`?>!lvI6-E$sR$z3=CCj3=9n|3=F@3LJcn%7)lKo7+xhXFj&oCU=S~uvn$XBD8X6a z5n0T@pr;JNj1^1m%YcIHC7!;n>@RuP`E?{(b~a823Ke_0IEHu}e>?Ry*8u|#XWyIu z|8J_@r0rV0P{C^2a>dA#2PX+xavYm;oBeY(+l8}{wi`Zobg`e@?8557ZOdkMfN5gH zEYFVdQ&MBb@0N+Dl3jhEB literal 0 HcmV?d00001 diff --git a/src/c/cryptic_clock.cpp b/src/c/cryptic_clock.cpp new file mode 100644 index 0000000..94558f6 --- /dev/null +++ b/src/c/cryptic_clock.cpp @@ -0,0 +1,72 @@ +#include +#include "pebble_bitmap_layer.hpp" +#include <@smallstoneapps/utils/macros.h> +#include "digit_layer.hpp" + +using namespace Pebble; + +#undef DEBUG + +class MainWindow : public AppWindow, public TickTimer { + DigitLayer digitLayers[4]; + GOwnedBitmap frameBitmap; + Pebble::BitmapLayer frameLayer; +protected: + void OnLoad() { + auto bounds = this->GetBounds(); + int16_t w2 = bounds.size.w / 2; + int16_t h = bounds.size.h; + + digitLayers[0].SetFrame(Pebble::GRect(w2-68, 0, 32, h)); + 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->frameBitmap = Pebble::GOwnedBitmap::CreateWithResource(RESOURCE_ID_FRAME); + this->frameLayer.SetBitmap(this->frameBitmap); + this->frameLayer.SetFrame(bounds); + this->frameLayer.SetCompositingMode(GCompOpSet); + this->AddChild(this->frameLayer); + +#ifdef DEBUG + TickTimerStart(SECOND_UNIT); +#else + TickTimerStart(MINUTE_UNIT); +#endif + UpdateTime(false); + } + + void UpdateTime(bool animate=true) { + 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); + this->digitLayers[3].RotateTo(minute, animate); + } + + void OnTick(tm &tick_time, TimeUnits units_changed) { + UpdateTime(true); + } +}; + +MAIN { + MainWindow window; + window.StackPush(true); + app_event_loop(); + return 0; +} \ No newline at end of file diff --git a/src/c/digit_layer.cpp b/src/c/digit_layer.cpp new file mode 100644 index 0000000..306823b --- /dev/null +++ b/src/c/digit_layer.cpp @@ -0,0 +1,73 @@ +#include "digit_layer.hpp" +#include <@smallstoneapps/utils/macros.h> + +using namespace Pebble; + +namespace { + // NOTE: technically a memory leak, destructor will never be called. + // Can't make it a static variable in draw_digit, because `atexit` is broken on Pebble. + static GOwnedBitmap digitBitmaps[10]; + static bool bitmapsInitialized = false; + + static void draw_digit(const Pebble::GContext& ctx, const Pebble::GRect& bounds, const uint16_t digit) { + if (!bitmapsInitialized) { + auto bitmap = GOwnedBitmap::CreateWithResource(RESOURCE_ID_DIGITS); + auto size = bitmap.GetBounds().size; + uint16_t w = size.w / 10; + uint16_t h = size.h; + for (int i = 0; i < 10; i++) { + digitBitmaps[i] = GOwnedBitmap::CreateSubBitmap(bitmap, Pebble::GRect(i * w, 0, w, h)); + } + bitmapsInitialized = true; + } + + auto bitmap = &digitBitmaps[digit]; + auto rect = bitmap->GetBounds(); + rect.Align(bounds, GAlignCenter, false); + ctx.DrawBitmapInRect(*bitmap, rect); + } +} + +void DigitLayerInner::OnUpdate(const Pebble::GContext& ctx) { + auto bounds = this->GetBounds(); + + ctx.SetFillColor(GColorBlack); + 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; + draw_digit(ctx, rect, digit); + } +} + +DigitLayer::DigitLayer() : Pebble::SimpleLayer(Pebble::GRect()) { + this->scrollLayer.AddChild(this->innerLayer); + this->scrollLayer.SetShadowHidden(true); + this->AddChild(this->scrollLayer); +} + +void DigitLayer::UpdateLayout() { + auto bounds = this->GetBounds(); + this->scrollLayer.SetFrame(bounds); + bounds.size.h = (this->innerLayer.digitCount + 2 * this->innerLayer.extraDigits) * this->innerLayer.digitHeight; + this->innerLayer.SetFrame(bounds); + this->scrollLayer.SetContentSize(bounds.size); +} + + +int16_t DigitLayer::ComputeOffset(uint16_t digit) { + auto bounds = this->GetBounds(); + return bounds.size.h / 2 - this->innerLayer.digitHeight / 2 + - (digit + this->innerLayer.extraDigits) * this->innerLayer.digitHeight; +} + +void DigitLayer::RotateTo(uint16_t digit, bool animate) { + digit %= innerLayer.digitCount; + auto currentOffset = this->scrollLayer.GetContentOffset(); + if (digit == 0 && currentOffset.y <= ComputeOffset(this->innerLayer.digitCount / 2)) { + currentOffset.y += this->innerLayer.digitCount * this->innerLayer.digitHeight; + this->scrollLayer.SetContentOffset(currentOffset, false); + } + + this->scrollLayer.SetContentOffset(this->ComputeOffset(digit), animate); +} \ No newline at end of file diff --git a/src/c/digit_layer.hpp b/src/c/digit_layer.hpp new file mode 100644 index 0000000..fdd8414 --- /dev/null +++ b/src/c/digit_layer.hpp @@ -0,0 +1,40 @@ +#pragma once + +#include +#include <@smallstoneapps/utils/macros.h> + +class DigitLayerInner : public Pebble::CustomLayer { + static constexpr int16_t extraDigits = 2; + static constexpr int16_t digitHeight = 56; + int16_t digitCount = 10; + + 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; + +protected: + void OnUpdate(const Pebble::GContext& ctx); +}; + +class DigitLayer : public Pebble::SimpleLayer { + DigitLayerInner innerLayer; + Pebble::ScrollLayer scrollLayer; +public: + DigitLayer(); + + void SetDigitCount(uint16_t cnt = 10) { + this->innerLayer.SetDigitCount(cnt); + this->MarkDirty(); + } + void UpdateLayout(); + void RotateTo(uint16_t digit, bool animate=true); +private: + int16_t ComputeOffset(uint16_t digit); +}; \ No newline at end of file diff --git a/src/c/pebble_bitmap_layer.hpp b/src/c/pebble_bitmap_layer.hpp new file mode 100644 index 0000000..652521c --- /dev/null +++ b/src/c/pebble_bitmap_layer.hpp @@ -0,0 +1,34 @@ +#pragma once + +namespace Pebble { + +class BitmapLayer : public Layer { +public: + BitmapLayer(GRect frame) : Layer((bitmap_layer_ = bitmap_layer_create(frame), bitmap_layer_get_layer(bitmap_layer_))) { } + + BitmapLayer() : BitmapLayer(GRect(0, 0, 0, 0)) {} + + //! Destroys the underlying BitmapLayer + virtual ~BitmapLayer() { + bitmap_layer_destroy(bitmap_layer_); + } + + //! Instances of this class can be automatically converted to Pebble BitmapLayers + inline operator ::BitmapLayer*() { return bitmap_layer_; } + + inline const GBitmap GetBitmap() { return GBitmap(const_cast< ::GBitmap*>(bitmap_layer_get_bitmap(bitmap_layer_))); } + + inline void SetBitmap(GBitmap bitmap) { bitmap_layer_set_bitmap(bitmap_layer_, bitmap); } + + inline void SetBackgroundColor(GColor color) { bitmap_layer_set_background_color(bitmap_layer_, color); } + + inline void SetAlignment(GAlign alignment) { bitmap_layer_set_alignment(bitmap_layer_, alignment); } + + inline void SetCompositingMode(GCompOp mode) { bitmap_layer_set_compositing_mode(bitmap_layer_, mode); } + +protected: + // The layer pointer + ::BitmapLayer *bitmap_layer_; +}; + +} \ No newline at end of file diff --git a/wscript b/wscript new file mode 100644 index 0000000..d2c130e --- /dev/null +++ b/wscript @@ -0,0 +1,67 @@ +# +# This file is the default set of rules to compile a Pebble application. +# +# Feel free to customize this to your needs. +# +import os.path + +top = '.' +out = 'build' + + +def options(ctx): + ctx.load('pebble_sdk') + + +def configure(ctx): + """ + This method is used to configure your build. ctx.load(`pebble_sdk`) automatically configures + a build for each valid platform in `targetPlatforms`. Platform-specific configuration: add your + change after calling ctx.load('pebble_sdk') and make sure to set the correct environment first. + Universal configuration: add your change prior to calling ctx.load('pebble_sdk'). + """ + ctx.load('pebble_sdk') + print(ctx.env.INCLUDES) + ctx.env.CXX = 'arm-none-eabi-g++' + ctx.env.AR = 'arm-none-eabi-ar' + ctx.load('g++') + print(ctx.env.INCLUDES) + + +def build(ctx): + ctx.load('pebble_sdk') + + build_worker = os.path.exists('worker_src') + binaries = [] + + cached_env = ctx.env + for platform in ctx.env.TARGET_PLATFORMS: + ctx.env = ctx.all_envs[platform] + ctx.set_group(ctx.env.PLATFORM_NAME) + + # -- C++ Reconfiguration -- + ctx.env.CXXFLAGS = list(ctx.env.CFLAGS) + ctx.env.CXXFLAGS.remove('-std=c99') + ctx.env.CXXFLAGS.extend(['-std=c++11', '-fPIE', '-fno-unwind-tables', '-fno-exceptions', '-fno-rtti', '-fno-threadsafe-statics']) + # print(ctx.env.CXXFLAGS) + # ------------------------- + + app_elf = '{}/pebble-app.elf'.format(ctx.env.BUILD_DIR) + ctx.pbl_build(source=ctx.path.ant_glob(['src/c/**/*.c','src/c/**/*.cpp']), target=app_elf, bin_type='app') + + if build_worker: + worker_elf = '{}/pebble-worker.elf'.format(ctx.env.BUILD_DIR) + binaries.append({'platform': platform, 'app_elf': app_elf, 'worker_elf': worker_elf}) + ctx.pbl_build(source=ctx.path.ant_glob(['worker_src/c/**/*.c''worker_src/c/**/*.cpp',]), + target=worker_elf, + bin_type='worker') + else: + binaries.append({'platform': platform, 'app_elf': app_elf}) + ctx.env = cached_env + + ctx.set_group('bundle') + ctx.pbl_bundle(binaries=binaries, + js=ctx.path.ant_glob(['src/pkjs/**/*.js', + 'src/pkjs/**/*.json', + 'src/common/**/*.js']), + js_entry_file='src/pkjs/index.js')