Import of the watch repository from Pebble

This commit is contained in:
Matthieu Jeanson
2024-12-12 16:43:03 -08:00
committed by Katharine Berry
commit 3b92768480
10334 changed files with 2564465 additions and 0 deletions

View File

@@ -0,0 +1,551 @@
/*
* Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "pebble_shapes.h"
#include "applib/app.h"
#include "applib/graphics/graphics.h"
#include "applib/graphics/gpath.h"
#include "applib/graphics/gtypes.h"
#include "applib/graphics/gtransform.h"
#include "applib/graphics/text.h"
#include "util/trig.h"
#include "applib/ui/action_bar_layer.h"
#include "applib/ui/app_window_stack.h"
#include "applib/ui/window.h"
#include "kernel/pbl_malloc.h"
#include "process_state/app_state/app_state.h"
#include "system/logging.h"
typedef enum {
POINT,
LINE,
SQUARE,
RECTANGLE,
RECTANGLE_ROUND,
CIRCLE,
GPATH_TRIANGLE,
GPATH_OPEN_BUCKET,
MAX_SHAPES
} DrawShape;
#define NUM_SHAPES MAX_SHAPES
static GColor s_display_colors[NUM_SHAPES];
#define MAX_STROKE_WIDTH 20
static const GPathInfo s_triangle_path = {
3,
(GPoint[]) {
{ -10, 0 },
{ 0, 10 },
{ 10, 0 }
}
};
static const GPathInfo s_bucket_path = {
4,
(GPoint[]) {
{ -10, 0 },
{ -10, 30 },
{ 10, 30 },
{ 10, 0 }
}
};
static const int TARGET_FPS = 40;
static const int PIXEL_SPEED_PER_FRAME = 2;
#define ANGLE_DEGREES_TO_TRIG_ANGLE(angle) (((angle % 360) * TRIG_MAX_ANGLE) / 360)
#define MAX_SCALE 10
typedef enum {
APP_STATE_FILL_NON_AA,
APP_STATE_FILL_AA,
APP_STATE_DRAW_NON_AA_NO_SW,
APP_STATE_DRAW_AA_NO_SW,
APP_STATE_DRAW_NON_AA_SW,
APP_STATE_DRAW_AA_SW,
// Add more above
APP_STATE_NUM_STATES
} AppStateIndex;
typedef struct AppData {
Window window;
// Point properties
GPoint point_p0;
int16_t point_velocity_x;
int16_t point_velocity_y;
// Line properties
GPoint line_p0;
GPoint line_p1;
int16_t line_velocity_x;
int16_t line_velocity_y;
// Square properties
GRect square;
int16_t square_velocity_x;
int16_t square_velocity_y;
// Rectangle properties
GRect rect;
int16_t rect_velocity_x;
int16_t rect_velocity_y;
// Rectangle Round properties
GRect rectr;
uint16_t rectr_radius;
uint16_t rectr_corners_index;
int16_t rectr_velocity_x;
int16_t rectr_velocity_y;
// Circle properties
GPoint circle_origin;
uint16_t circle_radius;
int16_t circle_velocity_x;
int16_t circle_velocity_y;
GColor circle_color;
// Triangle
GPath *triangle;
GPoint triangle_offset;
int16_t triangle_velocity_x;
int16_t triangle_velocity_y;
// Bucket
GPath *bucket;
GPoint bucket_offset;
int16_t bucket_velocity_x;
int16_t bucket_velocity_y;
// Generic properties that can be changed as property_index changes
bool fill;
int16_t color_index;
int64_t time_started;
uint32_t rendered_frames;
bool moving;
AppStateIndex state_index;
int16_t stroke_width;
bool antialiased;
} AppData;
static void log_state(AppData *data) {
switch (data->state_index) {
case APP_STATE_FILL_NON_AA:
PBL_LOG(LOG_LEVEL_DEBUG, "State: Fill Non-Antialiased; SW: N/A (but currently: %d)",
data->stroke_width);
break;
case APP_STATE_FILL_AA:
PBL_LOG(LOG_LEVEL_DEBUG, "State: Fill Antialiased; SW: N/A (but currently: %d)",
data->stroke_width);
break;
case APP_STATE_DRAW_NON_AA_NO_SW:
PBL_LOG(LOG_LEVEL_DEBUG, "State: Draw Non-Antialiased; SW: N/A (but currently: %d)",
data->stroke_width);
break;
case APP_STATE_DRAW_AA_NO_SW:
PBL_LOG(LOG_LEVEL_DEBUG, "State: Draw Antialiased; SW: N/A (but currently: %d)",
data->stroke_width);
break;
case APP_STATE_DRAW_NON_AA_SW:
PBL_LOG(LOG_LEVEL_DEBUG, "State: Draw Non-Antialiased; SW: %d", data->stroke_width);
break;
case APP_STATE_DRAW_AA_SW:
PBL_LOG(LOG_LEVEL_DEBUG, "State: Draw Antialiased; SW: %d", data->stroke_width);
break;
default:
PBL_LOG(LOG_LEVEL_DEBUG, "Unknown State");
break;
}
}
static bool stroke_width_enabled(AppStateIndex state_index) {
return ((state_index == APP_STATE_DRAW_AA_SW) || (state_index == APP_STATE_DRAW_NON_AA_SW));
}
static void update_state(AppData *data, AppStateIndex state_index) {
data->state_index = state_index;
data->fill = ((data->state_index == APP_STATE_FILL_NON_AA) ||
(data->state_index == APP_STATE_FILL_AA));
data->antialiased = ((data->state_index == APP_STATE_DRAW_AA_NO_SW) ||
(data->state_index == APP_STATE_DRAW_AA_SW) ||
(data->state_index == APP_STATE_FILL_AA));
}
static void back_handler(ClickRecognizerRef recognizer, void *context) {
AppData *data = app_state_get_user_data();
update_state(data, ((data->state_index - 1) % APP_STATE_NUM_STATES));
log_state(data);
}
static void up_handler(ClickRecognizerRef recognizer, void *context) {
AppData *data = app_state_get_user_data();
if (stroke_width_enabled(data->state_index)) {
data->stroke_width++;
if (data->stroke_width >= MAX_STROKE_WIDTH) {
data->stroke_width = 1;
}
}
log_state(data);
}
static void select_handler(ClickRecognizerRef recognizer, void *context) {
AppData *data = app_state_get_user_data();
update_state(data, ((data->state_index + 1) % APP_STATE_NUM_STATES));
log_state(data);
}
static void down_handler(ClickRecognizerRef recognizer, void *context) {
AppData *data = app_state_get_user_data();
data->moving = !data->moving;
log_state(data);
}
static void click_config_provider(void *context) {
window_single_click_subscribe(BUTTON_ID_BACK, back_handler);
window_single_click_subscribe(BUTTON_ID_UP, up_handler);
window_single_click_subscribe(BUTTON_ID_SELECT, select_handler);
window_single_click_subscribe(BUTTON_ID_DOWN, down_handler);
}
static void prv_move_shape(AppData *data) {
for (uint8_t shape = POINT; shape < MAX_SHAPES; shape++) {
if (shape == POINT) {
// Move the point 4*X per Y
data->point_p0.x += (data->point_velocity_x * PIXEL_SPEED_PER_FRAME * 4);
if (data->point_p0.x < 0 || data->point_p0.x > data->window.layer.bounds.size.w) {
data->point_velocity_x = data->point_velocity_x * -1;
}
data->point_p0.y += (data->point_velocity_y * PIXEL_SPEED_PER_FRAME);
if (data->point_p0.y < 0 || data->point_p0.y > data->window.layer.bounds.size.h) {
data->point_velocity_y = data->point_velocity_y * -1;
}
} else if (shape == LINE) {
// Move the line 2*X per Y
data->line_p0.x += (data->line_velocity_x * PIXEL_SPEED_PER_FRAME * 2);
data->line_p1.x += (data->line_velocity_x * PIXEL_SPEED_PER_FRAME * 2);
if (data->line_p0.x < 0 || data->line_p0.x > data->window.layer.bounds.size.w ||
data->line_p1.x < 0 || data->line_p1.x > data->window.layer.bounds.size.w ) {
data->line_velocity_x = data->line_velocity_x * -1;
}
data->line_p0.y += (data->line_velocity_y * PIXEL_SPEED_PER_FRAME);
data->line_p1.y += (data->line_velocity_y * PIXEL_SPEED_PER_FRAME);
if (data->line_p0.y < 0 || data->line_p0.y > data->window.layer.bounds.size.h ||
data->line_p1.y < 0 || data->line_p1.y > data->window.layer.bounds.size.h ) {
data->line_velocity_y = data->line_velocity_y * -1;
}
} else if (shape == SQUARE) {
// Move the square X per Y
data->square.origin.x += (data->square_velocity_x * PIXEL_SPEED_PER_FRAME);
if (data->square.origin.x < 0 ||
data->square.origin.x + data->square.size.w > data->window.layer.bounds.size.w) {
data->square_velocity_x = data->square_velocity_x * -1;
}
data->square.origin.y += (data->square_velocity_y * PIXEL_SPEED_PER_FRAME);
if (data->square.origin.y < 0 ||
data->square.origin.y + data->square.size.h > data->window.layer.bounds.size.h) {
data->square_velocity_y = data->square_velocity_y * -1;
}
} else if (shape == RECTANGLE) {
// Move the rectangle X per 2*Y
data->rect.origin.x += (data->rect_velocity_x * PIXEL_SPEED_PER_FRAME);
if (data->rect.origin.x < 0 ||
data->rect.origin.x + data->rect.size.w > data->window.layer.bounds.size.w) {
data->rect_velocity_x = data->rect_velocity_x * -1;
}
data->rect.origin.y += (data->rect_velocity_y * PIXEL_SPEED_PER_FRAME * 2);
if (data->rect.origin.y < 0 ||
data->rect.origin.y + data->rect.size.h > data->window.layer.bounds.size.h) {
data->rect_velocity_y = data->rect_velocity_y * -1;
}
} else if (shape == RECTANGLE_ROUND) {
// Move rounded line X per 4*Y
data->rectr.origin.x += (data->rectr_velocity_x * PIXEL_SPEED_PER_FRAME);
if (data->rectr.origin.x < 0 ||
data->rectr.origin.x + data->rectr.size.w > data->window.layer.bounds.size.w) {
data->rectr_velocity_x = data->rectr_velocity_x * -1;
}
data->rectr.origin.y += (data->rectr_velocity_y * PIXEL_SPEED_PER_FRAME * 4);
if (data->rectr.origin.y < 0 ||
data->rectr.origin.y + data->rectr.size.h > data->window.layer.bounds.size.h) {
data->rectr_velocity_y = data->rectr_velocity_y * -1;
}
} else if (shape == CIRCLE) {
// Move the line X per Y
data->circle_origin.x += (data->circle_velocity_x * PIXEL_SPEED_PER_FRAME);
if (data->circle_origin.x - data->circle_radius < 0 ||
data->circle_origin.x + data->circle_radius > data->window.layer.bounds.size.w) {
data->circle_velocity_x = data->circle_velocity_x * -1;
data->circle_color.argb = (((data->circle_color.argb + 1) & 0x3F) | 0xC0);
}
data->circle_origin.y += (data->circle_velocity_y * PIXEL_SPEED_PER_FRAME);
if (data->circle_origin.y - data->circle_radius < 0 ||
data->circle_origin.y + data->circle_radius > data->window.layer.bounds.size.h) {
data->circle_velocity_y = data->circle_velocity_y * -1;
data->circle_color.argb = (((data->circle_color.argb + 1) & 0x3F) | 0xC0);
}
} else if (shape == GPATH_TRIANGLE) {
// Move the line 3*X per Y
data->triangle_offset.x += (data->triangle_velocity_x * PIXEL_SPEED_PER_FRAME * 3);
if (data->triangle_offset.x < 0 ||
data->triangle_offset.x > data->window.layer.bounds.size.w) {
data->triangle_velocity_x = data->triangle_velocity_x * -1;
}
data->triangle_offset.y += (data->triangle_velocity_y * PIXEL_SPEED_PER_FRAME);
if (data->triangle_offset.y < 0 ||
data->triangle_offset.y > data->window.layer.bounds.size.h) {
data->triangle_velocity_y = data->triangle_velocity_y * -1;
}
gpath_move_to(data->triangle, data->triangle_offset);
} else if (shape == GPATH_OPEN_BUCKET) {
// Move the line 2*X per 3*Y
data->bucket_offset.x += (data->bucket_velocity_x * PIXEL_SPEED_PER_FRAME * 2);
if (data->bucket_offset.x < 0 ||
data->bucket_offset.x > data->window.layer.bounds.size.w) {
data->bucket_velocity_x = data->bucket_velocity_x * -1;
}
data->bucket_offset.y += (data->bucket_velocity_y * PIXEL_SPEED_PER_FRAME * 3);
if (data->bucket_offset.y < 0 ||
data->bucket_offset.y > data->window.layer.bounds.size.h) {
data->bucket_velocity_y = data->bucket_velocity_y * -1;
}
gpath_move_to(data->bucket, data->bucket_offset);
}
}
}
static void draw_shape(GContext* ctx, AppData *data, DrawShape shape, GColor color) {
graphics_context_set_fill_color(ctx, color);
graphics_context_set_stroke_color(ctx, color);
if (data->fill) {
if (shape == POINT) {
graphics_draw_pixel(ctx, data->point_p0);
} else if (shape == LINE) {
graphics_draw_line(ctx, data->line_p0, data->line_p1);
} else if (shape == SQUARE) {
graphics_fill_rect(ctx, &data->square);
} else if (shape == RECTANGLE) {
graphics_fill_rect(ctx, &data->rect);
} else if (shape == RECTANGLE_ROUND) {
graphics_fill_round_rect(ctx, &data->rectr, data->rectr_radius, GCornersAll);
} else if (shape == CIRCLE) {
graphics_context_set_fill_color(ctx, data->circle_color);
graphics_context_set_stroke_color(ctx, data->circle_color);
graphics_fill_circle(ctx, data->circle_origin, data->circle_radius);
} else if (shape == GPATH_TRIANGLE) {
gpath_draw_filled(ctx, data->triangle);
} else if (shape == GPATH_OPEN_BUCKET) {
gpath_draw_filled(ctx, data->bucket);
}
} else {
if (shape == POINT) {
graphics_draw_pixel(ctx, data->point_p0);
} else if (shape == LINE) {
graphics_draw_line(ctx, data->line_p0, data->line_p1);
} else if (shape == SQUARE) {
graphics_draw_rect(ctx, &data->square);
} else if (shape == RECTANGLE) {
graphics_draw_rect(ctx, &data->rect);
} else if (shape == RECTANGLE_ROUND) {
graphics_draw_round_rect(ctx, &data->rectr, data->rectr_radius);
} else if (shape == CIRCLE) {
graphics_context_set_fill_color(ctx, data->circle_color);
graphics_context_set_stroke_color(ctx, data->circle_color);
graphics_draw_circle(ctx, data->circle_origin, data->circle_radius);
} else if (shape == GPATH_TRIANGLE) {
gpath_draw_outline(ctx, data->triangle);
} else if (shape == GPATH_OPEN_BUCKET) {
gpath_draw_outline_open(ctx, data->bucket);
}
}
}
static int64_t prv_time_64(void) {
time_t s;
uint16_t ms;
rtc_get_time_ms(&s, &ms);
return (int64_t)s * 1000 + ms;
}
static void layer_update_proc(Layer *layer, GContext* ctx) {
AppData *data = app_state_get_user_data();
graphics_context_set_fill_color(ctx, GColorBlack);
if (stroke_width_enabled(data->state_index)) {
graphics_context_set_stroke_width(ctx, data->stroke_width);
} else {
graphics_context_set_stroke_width(ctx, 1);
}
graphics_context_set_antialiased(ctx, data->antialiased);
graphics_fill_rect(ctx, &layer->bounds);
uint8_t color_index = 0;
for (uint8_t shape_index = POINT; shape_index < MAX_SHAPES; shape_index++) {
draw_shape(ctx, data, (DrawShape)shape_index, (GColor)s_display_colors[color_index++]);
}
if (data->rendered_frames == 0) {
data->time_started = prv_time_64();
} else {
int64_t time_rendered = prv_time_64() - data->time_started;
if ((data->rendered_frames % 64) == 0) {
PBL_LOG(LOG_LEVEL_DEBUG, "## %d frames rendered", (int)data->rendered_frames);
PBL_LOG(LOG_LEVEL_DEBUG, "## at %"PRIu32" FPS",
(uint32_t)((uint64_t)data->rendered_frames*1000/time_rendered));
}
}
data->rendered_frames++;
}
static void timer_callback(void *cb_data) {
AppData *data = app_state_get_user_data();
if (data->moving) {
prv_move_shape(data);
}
layer_mark_dirty(&data->window.layer);
app_timer_register(1000 / TARGET_FPS, timer_callback, NULL);
}
static void init(void) {
AppData *data = task_malloc_check(sizeof(AppData));
memset(data, 0x00, sizeof(AppData));
s_display_colors[0] = GColorWhite;
s_display_colors[1] = GColorRed;
s_display_colors[2] = GColorGreen;
s_display_colors[3] = GColorBlue;
s_display_colors[4] = (GColor)((uint8_t)0b11111100);
s_display_colors[5] = (GColor)((uint8_t)0b11001111);
s_display_colors[6] = (GColor)((uint8_t)0b11110101);
s_display_colors[7] = GColorWhite;
app_state_set_user_data(data);
Window *window = &data->window;
window_init(window, WINDOW_NAME("Shapes"));
window_set_user_data(window, data);
window_set_fullscreen(window, true);
layer_set_update_proc(&window->layer, layer_update_proc);
window_set_click_config_provider(window, click_config_provider);
const bool animated = true;
app_window_stack_push(window, animated);
// Initialize shapes
// Point properties
data->point_p0 = GPoint(1, 1);
data->point_velocity_x = 1;
data->point_velocity_y = 1;
// Line properties
data->line_p0 = GPoint(0, 0);
data->line_p1 = GPoint(10, 10);
data->line_velocity_x = 1;
data->line_velocity_y = 1;
// Square properties
data->square = GRect(100, 50, 20, 20);
data->square_velocity_x = 1;
data->square_velocity_y = 1;
// Rectangle properties
data->rect = GRect(80, 0, 30, 50);
data->rect_velocity_x = 1;
data->rect_velocity_y = 1;
// Rectangle Round properties
data->rectr = GRect(20, 20, 20, 30);
data->rectr_radius = 5;
data->rectr_corners_index = GCornersAll;
data->rectr_velocity_x = 1;
data->rectr_velocity_y = 1;
// Circle properties
data->circle_origin = GPoint(50, 50);
data->circle_radius = 20;
data->circle_velocity_x = 1;
data->circle_velocity_y = 1;
data->circle_color = s_display_colors[5];
// Triangle
data->triangle = gpath_create(&s_triangle_path);
data->triangle_offset = GPoint(10, 80);
gpath_move_to(data->triangle, data->triangle_offset);
data->triangle_velocity_x = 1;
data->triangle_velocity_y = 1;
// Open bucket
data->bucket = gpath_create(&s_bucket_path);
data->bucket_offset = GPoint(20, 30);
gpath_move_to(data->bucket, data->bucket_offset);
data->bucket_velocity_x = 1;
data->bucket_velocity_y = 1;
// Other properties
update_state(data, APP_STATE_FILL_NON_AA);
data->stroke_width = 1;
data->moving = true;
app_timer_register(33, timer_callback, NULL);
}
static void deinit(void) {
AppData *data = app_state_get_user_data();
task_free(data);
}
static void s_main(void) {
init();
app_event_loop();
deinit();
}
const PebbleProcessMd* pebble_shapes_get_app_info(void) {
static const PebbleProcessMdSystem s_app_info = {
.common.main_func = s_main,
.name = "Pebble Shapes"
};
return (const PebbleProcessMd*) &s_app_info;
}