Initial commit
This commit is contained in:
commit
17a11c894a
6 changed files with 535 additions and 0 deletions
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
.cache/
|
||||
build/
|
||||
.lock-waf_linux_build
|
||||
36
README.md
Normal file
36
README.md
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
# tijd
|
||||
|
||||
A Pebble watchapp/watchface written in C using the Pebble SDK.
|
||||
|
||||
## Building & running
|
||||
|
||||
```sh
|
||||
pebble build # build for all targetPlatforms
|
||||
pebble install --emulator emery # install on the emery emulator
|
||||
pebble install --phone <ip> # install to a paired phone
|
||||
```
|
||||
|
||||
## Target platforms
|
||||
|
||||
`targetPlatforms` in `package.json` controls which watches you build for. The
|
||||
modern Pebble hardware is **emery** (Pebble Time 2), **gabbro** (Pebble Round
|
||||
2), and **flint** (Pebble 2 Duo); the original Pebble platforms (aplite,
|
||||
basalt, chalk, diorite) are included by default for backwards compatibility.
|
||||
|
||||
## Project layout
|
||||
|
||||
```
|
||||
src/c/ C source for the watchapp
|
||||
src/pkjs/ PebbleKit JS (phone-side) source, if any
|
||||
worker_src/c/ Background worker source, if any
|
||||
resources/ Images, fonts, and other bundled resources
|
||||
package.json Project metadata (UUID, platforms, resources, message keys)
|
||||
wscript Build rules — usually no need to edit
|
||||
```
|
||||
|
||||
By default this project is configured as a watchapp. To make it a watchface,
|
||||
set `pebble.watchapp.watchface` to `true` in `package.json`.
|
||||
|
||||
## Documentation
|
||||
|
||||
Full SDK docs, tutorials, and API reference: <https://developer.repebble.com>
|
||||
58
compile_commands.json
Normal file
58
compile_commands.json
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
[
|
||||
{
|
||||
"directory": "/home/collin/Documents/tijd",
|
||||
"file": "/home/collin/Documents/tijd/src/c/tijd.c",
|
||||
"arguments": [
|
||||
"/home/collin/.local/share/pebble-sdk/SDKs/current/toolchain/arm-none-eabi/bin/arm-none-eabi-gcc",
|
||||
"-std=c99",
|
||||
"-mcpu=cortex-m3",
|
||||
"-mthumb",
|
||||
"-ffunction-sections",
|
||||
"-fdata-sections",
|
||||
"-fcommon",
|
||||
"-g",
|
||||
"-fPIE",
|
||||
"-Os",
|
||||
"-D_TIME_H_",
|
||||
"-Dtime_t=long",
|
||||
"-Wall",
|
||||
"-Wextra",
|
||||
"-Werror",
|
||||
"-Wno-unused-parameter",
|
||||
"-Wno-error=unused-function",
|
||||
"-Wno-error=unused-variable",
|
||||
"-Wno-error=builtin-declaration-mismatch",
|
||||
"-Wno-error=format-truncation",
|
||||
"-Wno-error=expansion-to-defined",
|
||||
"-Wno-error=zero-length-bounds",
|
||||
"-Wno-error=cast-function-type",
|
||||
"-Wno-error=unused-value",
|
||||
"-DRELEASE",
|
||||
"-DPBL_PLATFORM_EMERY",
|
||||
"-DPBL_COLOR",
|
||||
"-DPBL_RECT",
|
||||
"-DPBL_MICROPHONE",
|
||||
"-DPBL_SMARTSTRAP",
|
||||
"-DPBL_HEALTH",
|
||||
"-DPBL_SMARTSTRAP_POWER",
|
||||
"-DPBL_COMPASS",
|
||||
"-DPBL_TOUCH",
|
||||
"-DPBL_RGB_BACKLIGHT",
|
||||
"-DPBL_SPEAKER",
|
||||
"-DPBL_DISPLAY_WIDTH=200",
|
||||
"-DPBL_DISPLAY_HEIGHT=228",
|
||||
"-DPBL_SDK_3",
|
||||
"-I/home/collin/.local/share/pebble-sdk/SDKs/current/sdk-core/pebble/emery/include",
|
||||
"-I/home/collin/Documents/tijd",
|
||||
"-I/home/collin/Documents/tijd/src",
|
||||
"-I/home/collin/Documents/tijd/src/c",
|
||||
"-I/home/collin/Documents/tijd/build/include",
|
||||
"-I/home/collin/Documents/tijd/build/emery",
|
||||
"-I/home/collin/Documents/tijd/build/emery/src",
|
||||
"-c",
|
||||
"/home/collin/Documents/tijd/src/c/tijd.c",
|
||||
"-o",
|
||||
"build/emery/src/c/tijd.c.o"
|
||||
]
|
||||
}
|
||||
]
|
||||
34
package.json
Normal file
34
package.json
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
{
|
||||
"name": "tijd",
|
||||
"author": "MakeAwesomeHappen",
|
||||
"version": "1.0.0",
|
||||
"keywords": [
|
||||
"pebble-app"
|
||||
],
|
||||
"private": true,
|
||||
"dependencies": {},
|
||||
"pebble": {
|
||||
"displayName": "tijd",
|
||||
"uuid": "09093348-4510-4240-aafb-31e3b54e5c13",
|
||||
"sdkVersion": "3",
|
||||
"enableMultiJS": true,
|
||||
"targetPlatforms": [
|
||||
"aplite",
|
||||
"basalt",
|
||||
"chalk",
|
||||
"diorite",
|
||||
"emery",
|
||||
"flint",
|
||||
"gabbro"
|
||||
],
|
||||
"watchapp": {
|
||||
"watchface": true
|
||||
},
|
||||
"messageKeys": [
|
||||
"dummy"
|
||||
],
|
||||
"resources": {
|
||||
"media": []
|
||||
}
|
||||
}
|
||||
}
|
||||
350
src/c/tijd.c
Normal file
350
src/c/tijd.c
Normal file
|
|
@ -0,0 +1,350 @@
|
|||
#include <pebble.h>
|
||||
#include <stdio.h>
|
||||
|
||||
static Window *s_window;
|
||||
static TextLayer *s_time_layer;
|
||||
|
||||
static TextLayer *s_hour_layer;
|
||||
static TextLayer *s_modifier_layer;
|
||||
static TextLayer *s_addend_layer;
|
||||
static TextLayer *s_operator_layer;
|
||||
static Layer *s_operator_hand_layer;
|
||||
|
||||
enum MinutesRounded {
|
||||
MIN_ZERO,
|
||||
MIN_FIVE,
|
||||
MIN_TEN,
|
||||
MIN_FIFTEEN,
|
||||
MIN_TWENTY,
|
||||
MIN_TWENTYFIVE,
|
||||
MIN_THIRTY,
|
||||
MIN_THIRTYFIVE,
|
||||
MIN_FORTY,
|
||||
MIN_FORTYFIVE,
|
||||
MIN_FIFTY,
|
||||
MIN_FIFTYFIVE,
|
||||
};
|
||||
|
||||
enum Modifier {
|
||||
MOD_NONE,
|
||||
MOD_HOUR,
|
||||
MOD_HALF,
|
||||
};
|
||||
|
||||
enum Operator {
|
||||
OPERATOR_NONE,
|
||||
OPERATOR_TILL,
|
||||
OPERATOR_PAST,
|
||||
};
|
||||
|
||||
enum Addend {
|
||||
ADDEND_ZERO,
|
||||
ADDEND_FIVE,
|
||||
ADDEND_TEN,
|
||||
ADDEND_QUARTER,
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
int hour;
|
||||
enum Modifier mod;
|
||||
enum Addend add;
|
||||
enum Operator op;
|
||||
} TimeComponents;
|
||||
|
||||
static TimeComponents tc = { -1, MOD_NONE, ADDEND_ZERO, OPERATOR_NONE };
|
||||
|
||||
static bool time_components_eq(TimeComponents *tc1, TimeComponents *tc2) {
|
||||
return tc1 != NULL && tc2 != NULL && tc1->hour == tc2->hour &&
|
||||
tc1->add == tc2->add && tc1->mod == tc2->mod && tc1->op == tc2->op;
|
||||
}
|
||||
|
||||
static enum MinutesRounded get_minutes_rounded(struct tm *tick_time) {
|
||||
int min_unrounded = tick_time->tm_min;
|
||||
int min_remainder = min_unrounded % 5;
|
||||
int min_base = min_unrounded - min_remainder;
|
||||
/**
|
||||
* rounded to nearest 5 mins where 3,4,5 round to 5 and 0,1,2 round to 0
|
||||
*/
|
||||
int min_rounded = min_base + (min_remainder >= 3 ? 5 : 0);
|
||||
APP_LOG(APP_LOG_LEVEL_INFO, "Minutes rounded: %d", min_rounded);
|
||||
switch (min_rounded) {
|
||||
case 0:
|
||||
return MIN_ZERO;
|
||||
case 5:
|
||||
return MIN_FIVE;
|
||||
case 10:
|
||||
return MIN_TEN;
|
||||
case 15:
|
||||
return MIN_FIFTEEN;
|
||||
case 20:
|
||||
return MIN_TWENTY;
|
||||
case 25:
|
||||
return MIN_TWENTYFIVE;
|
||||
case 30:
|
||||
return MIN_THIRTY;
|
||||
case 35:
|
||||
return MIN_THIRTYFIVE;
|
||||
case 40:
|
||||
return MIN_FORTY;
|
||||
case 45:
|
||||
return MIN_FORTYFIVE;
|
||||
case 50:
|
||||
return MIN_FIFTY;
|
||||
case 55:
|
||||
return MIN_FIFTY;
|
||||
case 60:
|
||||
default:
|
||||
return MIN_ZERO;
|
||||
}
|
||||
}
|
||||
|
||||
static int get_hour_component(struct tm *tick_time, enum MinutesRounded mr) {
|
||||
int hour = ((tick_time->tm_hour) + (mr > 3 ? 1 : 0)) % 12;
|
||||
return hour == 0 ? 12 : hour;
|
||||
}
|
||||
|
||||
static enum Modifier get_modifier_component(enum MinutesRounded mr) {
|
||||
switch (mr) {
|
||||
case MIN_ZERO:
|
||||
return MOD_HOUR;
|
||||
case MIN_FIVE:
|
||||
return MOD_NONE;
|
||||
case MIN_TEN:
|
||||
return MOD_NONE;
|
||||
case MIN_FIFTEEN:
|
||||
return MOD_NONE;
|
||||
case MIN_TWENTY:
|
||||
return MOD_HALF;
|
||||
case MIN_TWENTYFIVE:
|
||||
return MOD_HALF;
|
||||
case MIN_THIRTY:
|
||||
return MOD_HALF;
|
||||
case MIN_THIRTYFIVE:
|
||||
return MOD_HALF;
|
||||
case MIN_FORTY:
|
||||
return MOD_HALF;
|
||||
case MIN_FORTYFIVE:
|
||||
return MOD_NONE;
|
||||
case MIN_FIFTY:
|
||||
return MOD_NONE;
|
||||
case MIN_FIFTYFIVE:
|
||||
return MOD_NONE;
|
||||
default:
|
||||
return MOD_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
static enum Addend get_addend_component(enum MinutesRounded mr) {
|
||||
switch (mr) {
|
||||
case MIN_ZERO:
|
||||
case MIN_THIRTY:
|
||||
return ADDEND_ZERO;
|
||||
case MIN_FIVE:
|
||||
case MIN_TWENTYFIVE:
|
||||
case MIN_THIRTYFIVE:
|
||||
case MIN_FIFTYFIVE:
|
||||
return ADDEND_FIVE;
|
||||
case MIN_TEN:
|
||||
case MIN_TWENTY:
|
||||
case MIN_FORTY:
|
||||
case MIN_FIFTY:
|
||||
return ADDEND_TEN;
|
||||
case MIN_FIFTEEN:
|
||||
case MIN_FORTYFIVE:
|
||||
return ADDEND_QUARTER;
|
||||
default:
|
||||
return ADDEND_ZERO;
|
||||
}
|
||||
}
|
||||
|
||||
static enum Operator get_operator_component(enum MinutesRounded mr) {
|
||||
switch (mr) {
|
||||
case MIN_ZERO:
|
||||
return OPERATOR_NONE;
|
||||
case MIN_FIVE:
|
||||
return OPERATOR_PAST;
|
||||
case MIN_TEN:
|
||||
return OPERATOR_PAST;
|
||||
case MIN_FIFTEEN:
|
||||
return OPERATOR_PAST;
|
||||
case MIN_TWENTY:
|
||||
return OPERATOR_TILL;
|
||||
case MIN_TWENTYFIVE:
|
||||
return OPERATOR_TILL;
|
||||
case MIN_THIRTY:
|
||||
return OPERATOR_NONE;
|
||||
case MIN_THIRTYFIVE:
|
||||
return OPERATOR_PAST;
|
||||
case MIN_FORTY:
|
||||
return OPERATOR_PAST;
|
||||
case MIN_FORTYFIVE:
|
||||
return OPERATOR_TILL;
|
||||
case MIN_FIFTY:
|
||||
return OPERATOR_TILL;
|
||||
case MIN_FIFTYFIVE:
|
||||
return OPERATOR_TILL;
|
||||
default:
|
||||
return OPERATOR_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
static TimeComponents get_time_components(struct tm *tick_time) {
|
||||
enum MinutesRounded mr = get_minutes_rounded(tick_time);
|
||||
enum Modifier mod = get_modifier_component(mr);
|
||||
int hour = get_hour_component(tick_time, mr);
|
||||
enum Addend add = get_addend_component(mr);
|
||||
enum Operator op = get_operator_component(mr);
|
||||
|
||||
TimeComponents tc = {hour, mod, add, op};
|
||||
return tc;
|
||||
}
|
||||
|
||||
static char *render_hour_component(int hour) {
|
||||
switch (hour % 12) {
|
||||
case 0:
|
||||
return "TWALF";
|
||||
case 1:
|
||||
return "EEN";
|
||||
case 2:
|
||||
return "TWEE";
|
||||
case 3:
|
||||
return "DRIE";
|
||||
case 4:
|
||||
return "VIER";
|
||||
case 5:
|
||||
return "VIJF";
|
||||
case 6:
|
||||
return "ZES";
|
||||
case 7:
|
||||
return "ZEVEN";
|
||||
case 8:
|
||||
return "ACHT";
|
||||
case 9:
|
||||
return "NEGEN";
|
||||
case 10:
|
||||
return "TIEN";
|
||||
case 11:
|
||||
return "ELF";
|
||||
default:
|
||||
return "???";
|
||||
}
|
||||
}
|
||||
|
||||
static char *render_modifier_component(enum Modifier mod) {
|
||||
switch (mod) {
|
||||
case MOD_NONE:
|
||||
return "";
|
||||
case MOD_HALF:
|
||||
return "HALF";
|
||||
case MOD_HOUR:
|
||||
return "UUR";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
static char *render_addend_component(enum Addend add) {
|
||||
switch (add) {
|
||||
case ADDEND_FIVE:
|
||||
return "VIJF";
|
||||
case ADDEND_TEN:
|
||||
return "TIEN";
|
||||
case ADDEND_QUARTER:
|
||||
return "KWART";
|
||||
case ADDEND_ZERO:
|
||||
return "";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
static char *render_operator_component(enum Operator op) {
|
||||
switch (op) {
|
||||
case OPERATOR_NONE:
|
||||
return "";
|
||||
case OPERATOR_PAST:
|
||||
return "OVER";
|
||||
case OPERATOR_TILL:
|
||||
return "VOOR";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
static void update_time(struct tm *tick_time) {
|
||||
TimeComponents new_tc = get_time_components(tick_time);
|
||||
bool needs_redraw = !time_components_eq(&tc, &new_tc);
|
||||
if (needs_redraw) {
|
||||
tc = new_tc;
|
||||
static char s_time_buffer[24];
|
||||
snprintf(s_time_buffer, sizeof(s_time_buffer), "\n%s\n%s\n%s\n%s",
|
||||
render_addend_component(tc.add), render_operator_component(tc.op),
|
||||
render_modifier_component(tc.mod), render_hour_component(tc.hour));
|
||||
layer_mark_dirty(s_operator_hand_layer);
|
||||
text_layer_set_text(s_time_layer, s_time_buffer);
|
||||
}
|
||||
}
|
||||
|
||||
static void tick_handler(struct tm *tick_time, TimeUnits units_changed) {
|
||||
update_time(tick_time);
|
||||
}
|
||||
|
||||
static void operator_hand_layer_update_proc(Layer *layer, GContext *ctx) {
|
||||
graphics_context_set_stroke_color(ctx, GColorArmyGreen);
|
||||
graphics_context_set_stroke_width(ctx, 4);
|
||||
graphics_draw_arc(ctx, layer_get_bounds(layer), GOvalScaleModeFitCircle,
|
||||
DEG_TO_TRIGANGLE(0), DEG_TO_TRIGANGLE(360));
|
||||
}
|
||||
|
||||
static void main_window_load(Window *window) {
|
||||
Layer *window_layer = window_get_root_layer(window);
|
||||
GRect bounds = layer_get_bounds(window_layer);
|
||||
|
||||
window_set_background_color(window, GColorPurple);
|
||||
|
||||
s_time_layer = text_layer_create(bounds);
|
||||
text_layer_set_background_color(s_time_layer, GColorClear);
|
||||
text_layer_set_text_color(s_time_layer, GColorWhite);
|
||||
text_layer_set_font(s_time_layer,
|
||||
fonts_get_system_font(FONT_KEY_BITHAM_42_BOLD));
|
||||
text_layer_set_text_alignment(s_time_layer, GTextAlignmentCenter);
|
||||
|
||||
s_hour_layer = text_layer_create(bounds);
|
||||
text_layer_set_background_color(s_hour_layer, GColorClear);
|
||||
text_layer_set_text_color(s_hour_layer, GColorWhite);
|
||||
text_layer_set_font(s_hour_layer, fonts_get_system_font(FONT_KEY_BITHAM_42_BOLD));
|
||||
text_layer_set_text_alignment(s_time_layer, GTextAlignmentCenter);
|
||||
text_layer_set_text(s_time_layer, "TIJD");
|
||||
|
||||
layer_add_child(window_layer, text_layer_get_layer(s_time_layer));
|
||||
layer_add_child(window_layer, text_layer_get_layer(s_hour_layer));
|
||||
|
||||
GRect operator_hand_layer_bounds = GRect(bounds.origin.x + 20, bounds.origin.y + 20, bounds.size.w - 40, bounds.size.h - 40);
|
||||
s_operator_hand_layer = layer_create(operator_hand_layer_bounds);
|
||||
layer_set_update_proc(s_operator_hand_layer, operator_hand_layer_update_proc);
|
||||
layer_add_child(window_layer, s_operator_hand_layer);
|
||||
}
|
||||
|
||||
static void main_window_unload(Window *window) {
|
||||
text_layer_destroy(s_time_layer);
|
||||
}
|
||||
|
||||
static void init() {
|
||||
s_window = window_create();
|
||||
window_set_background_color(s_window, GColorRed);
|
||||
window_set_window_handlers(
|
||||
s_window,
|
||||
(WindowHandlers){.load = main_window_load, .unload = main_window_unload});
|
||||
|
||||
window_stack_push(s_window, true);
|
||||
|
||||
tick_timer_service_subscribe(MINUTE_UNIT, tick_handler);
|
||||
}
|
||||
|
||||
static void deinit() { window_destroy(s_window); }
|
||||
|
||||
int main(void) {
|
||||
init();
|
||||
app_event_loop();
|
||||
deinit();
|
||||
}
|
||||
54
wscript
Normal file
54
wscript
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
#
|
||||
# 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')
|
||||
|
||||
|
||||
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)
|
||||
app_elf = '{}/pebble-app.elf'.format(ctx.env.BUILD_DIR)
|
||||
ctx.pbl_build(source=ctx.path.ant_glob('src/c/**/*.c'), 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'),
|
||||
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')
|
||||
Loading…
Add table
Reference in a new issue