Commits

Trammell Hudson committed 594a56d

Started on fireworks. Not sure if it will work

Comments (0)

Files changed (11)

fireworks/Makefile

+SRCS += main.c
+SRCS += arcs.c
+SRCS += draw.c
+SRCS += sin_table.c
+SRCS += hershey.c
+
+include ../Makefile.rules
+/** \file
+ * Drawing functions.
+ */
+#include "draw.h"
+
+
+void
+draw_pixel(
+	color24_t c,
+	int32_t x,
+	int32_t y
+)
+{
+	//printf("pix %d %d @ %d %d %d\n", x, y, c.red, c.blue, c.green);
+	if (x >= VSCREEN_WIDTH || y >= VSCREEN_HEIGHT)
+		return;
+	if (x < 0 || y < 0)
+		return;
+
+	x >>= VSCREEN_SHIFT;
+	y >>= VSCREEN_SHIFT;
+	pulse_set_draw_window(x, y, x, y);
+	pulse_draw_point24(c);
+}
+
+
+
+static inline int32_t
+vscreen_ipart(
+	int32_t x
+)
+{
+	return x & ~(VSCREEN_PIXEL - 1);
+}
+
+
+static inline int32_t
+vscreen_fpart(
+	int32_t x
+)
+{
+	return x & (VSCREEN_PIXEL - 1);
+}
+
+
+static inline int32_t
+vscreen_rfpart(
+	int32_t x
+)
+{
+	return VSCREEN_PIXEL - vscreen_fpart(x);
+}
+
+
+static inline int32_t
+vscreen_round(
+	int32_t x
+)
+{
+	return vscreen_ipart(x + (VSCREEN_PIXEL / 2));
+}
+
+
+static void
+draw_pixel_aa(
+	color24_t c,
+	int32_t x,
+	int32_t y,
+	uint32_t frac
+)
+{
+	if (c.red)
+		c.red = (c.red * frac) >> VSCREEN_SHIFT;
+	if (c.blue)
+		c.blue = (c.blue * frac) >> VSCREEN_SHIFT;
+	if (c.green)
+		c.green = (c.green * frac) >> VSCREEN_SHIFT;
+
+	draw_pixel(c, x, y);
+}
+
+
+/** Draw using Xiaolin Wu's line algorithm */
+#define GRAD_SCALE 256
+
+void
+draw_line_horiz(
+	color24_t c,
+	int32_t x1,
+	int32_t y1,
+	int32_t x2,
+	int32_t y2
+)
+{
+	const int32_t dx = x2 - x1;
+	const int32_t dy = y2 - y1;
+
+	if (x2 < x1)
+	{
+		int32_t temp = x1;
+		x1 = x2;
+		x2 = temp;
+		temp = y1;
+		y1 = y2;
+		y2 = temp;
+	}
+
+	// The gradient is scaled by another shift factor since
+	// otherwise we won't hit the end points correctly.
+	const int32_t grad = (dy * GRAD_SCALE * VSCREEN_PIXEL) / dx;
+	const int32_t half_pixel = VSCREEN_PIXEL / 2;
+
+	int32_t xend, yend, xgap;
+
+	// Handle first endpoint
+	xend = vscreen_round(x1);
+	yend = y1 + (grad * (xend - x1)) / GRAD_SCALE / VSCREEN_PIXEL;
+	xgap = vscreen_rfpart(x1 + half_pixel);
+	const int32_t xpxl1 = xend;
+	const int32_t ypxl1 = vscreen_ipart(yend);
+	draw_pixel_aa(c, xpxl1, ypxl1, (vscreen_rfpart(yend) * xgap) / VSCREEN_PIXEL);
+	draw_pixel_aa(c, xpxl1, ypxl1 + VSCREEN_PIXEL, (vscreen_fpart(yend) * xgap) / VSCREEN_PIXEL);
+	int32_t intery = yend * GRAD_SCALE + grad; // first y-intersection
+
+	//printf("%d %d %d => %d %d grad %d\n", x1, xend, y1, x2, y2, grad);
+
+
+	// Handle second endpoint
+	xend = vscreen_round(x2);
+	yend = y2 + (grad * (xend - x2)) / GRAD_SCALE / VSCREEN_PIXEL;
+	xgap = vscreen_fpart(x2 + half_pixel);
+	const int32_t xpxl2 = xend;
+	const int32_t ypxl2 = vscreen_ipart(yend);
+	draw_pixel_aa(c, xpxl2, ypxl2, (vscreen_rfpart(yend) * xgap) / VSCREEN_PIXEL);
+	draw_pixel_aa(c, xpxl2, ypxl2 + VSCREEN_PIXEL, (vscreen_fpart(yend) * xgap) / VSCREEN_PIXEL);
+
+	//printf("x: %d %d\n", xpxl1 + 1, xpxl2);
+
+	// Loop (this could make a single window draw)
+	for (int32_t x = xpxl1 + VSCREEN_PIXEL ; x < xpxl2 ; x += VSCREEN_PIXEL)
+	{
+		int32_t y = intery / GRAD_SCALE;
+		intery += grad;
+		draw_pixel_aa(c, x, vscreen_ipart(y), vscreen_rfpart(y));
+		draw_pixel_aa(c, x, vscreen_ipart(y)+VSCREEN_PIXEL, vscreen_fpart(y));
+	}
+}
+
+
+static void
+draw_line_vert(
+	color24_t c,
+	int32_t x1,
+	int32_t y1,
+	int32_t x2,
+	int32_t y2
+)
+{
+	const int32_t dx = x2 - x1;
+	const int32_t dy = y2 - y1;
+
+	if (y2 < y1)
+	{
+		int32_t temp = x1;
+		x1 = x2;
+		x2 = temp;
+		temp = y1;
+		y1 = y2;
+		y2 = temp;
+	}
+
+	const int32_t grad = (dx * VSCREEN_PIXEL * GRAD_SCALE) / dy;
+	const int32_t half_pixel = VSCREEN_PIXEL / 2;
+
+	int32_t xend, yend, ygap;
+
+	// Handle first endpoint
+	yend = vscreen_round(y1);
+	xend = x1 + (grad * (yend - y1)) / GRAD_SCALE / VSCREEN_PIXEL;
+	ygap = vscreen_rfpart(y1 + half_pixel);
+	const int32_t ypxl1 = yend;
+	const int32_t xpxl1 = vscreen_ipart(xend);
+	draw_pixel_aa(c, xpxl1, ypxl1, (vscreen_rfpart(xend) * ygap) / VSCREEN_PIXEL);
+	draw_pixel_aa(c, xpxl1 + VSCREEN_PIXEL, ypxl1, (vscreen_fpart(xend) * ygap) / VSCREEN_PIXEL);
+	int32_t interx = xend * GRAD_SCALE + grad; // first x-intersection
+
+
+	// Handle second endpoint
+	yend = vscreen_round(y2);
+	xend = x2 + (grad * (yend - y2)) / GRAD_SCALE / VSCREEN_PIXEL;
+	ygap = vscreen_fpart(y2 + half_pixel);
+	const int32_t ypxl2 = yend;
+	const int32_t xpxl2 = vscreen_ipart(xend);
+	draw_pixel_aa(c, xpxl2, ypxl2, (vscreen_rfpart(xend) * ygap) / VSCREEN_PIXEL);
+	draw_pixel_aa(c, xpxl2 + VSCREEN_PIXEL, ypxl2, (vscreen_fpart(xend) * ygap) / VSCREEN_PIXEL);
+
+	//printf("x: %d %d\n", xpxl1 + 1, xpxl2);
+
+	// Loop (this could make a single window draw)
+	for (int32_t y = ypxl1 + VSCREEN_PIXEL ; y < ypxl2 ; y += VSCREEN_PIXEL)
+	{
+		int32_t x = interx / GRAD_SCALE;
+		interx += grad;
+
+		draw_pixel_aa(c, vscreen_ipart(x), y, vscreen_rfpart(x));
+		draw_pixel_aa(c, vscreen_ipart(x)+VSCREEN_PIXEL, y, vscreen_fpart(x));
+	}
+}
+
+
+void
+draw_line(
+	color24_t c,
+	int32_t x0,
+	int32_t y0,
+	int32_t x1,
+	int32_t y1
+)
+{
+	const int32_t dx = x1 > x0 ? x1 - x0 : x0 - x1;
+	const int32_t dy = y1 > y0 ? y1 - y0 : y0 - y1;
+	if (dx == 0 && dy == 0)
+		draw_pixel(c, x0, y0);
+	else
+	if (dx > dy)
+		draw_line_horiz(c, x0, y0, x1, y1);
+	else
+		draw_line_vert(c, x0, y0, x1, y1);
+
+/*
+	// Mark where we should start and end
+	draw_pixel(COLOR_WHITE, x0, y0);
+	draw_pixel(COLOR_BLUE, x1, y1);
+*/
+}
+
+
+
+
+static char buf[2];
+struct PWidgetTextDynamic font;
+
+void
+draw_monostring(
+	int32_t x,
+	int32_t y,
+	color24_t color,
+	PulseResource font_name,
+	int width,
+	const char * s
+)
+{
+	pulse_init_dynamic_text_widget(
+		&font,
+		buf,
+		font_name,
+		color,
+		PWTS_TRUNCATE
+	);
+
+	uint32_t orig_x = x >> VSCREEN_SHIFT;
+
+	struct PWTextBox box = {
+		.left		= orig_x,
+		.top		= y >> VSCREEN_SHIFT,
+		.right		= SCREEN_WIDTH - 1,
+		.bottom		= SCREEN_HEIGHT - 1,
+	};
+
+	buf[1] = '\0';
+
+	while ((buf[0] = *s++))
+	{
+		pulse_render_text(&box, &font);
+		box.left += width;
+	}
+}
+
+
+void
+fill(
+	color24_t color,
+	int32_t x,
+	int32_t y,
+	int32_t w,
+	int32_t h
+)
+{
+	if (x < 0 || y < 0 || x >= VSCREEN_WIDTH || y >= VSCREEN_HEIGHT)
+		return;
+
+	if (w <= (1 << VSCREEN_SHIFT) || h <= (1 << VSCREEN_SHIFT))
+		return;
+
+	x >>= VSCREEN_SHIFT;
+	y >>= VSCREEN_SHIFT;
+	w >>= VSCREEN_SHIFT;
+	h >>= VSCREEN_SHIFT;
+
+	if (x + w > SCREEN_WIDTH)
+		w = SCREEN_WIDTH - 1;
+	if (y + h > SCREEN_HEIGHT)
+		h = SCREEN_HEIGHT - 1;
+
+	pulse_set_draw_window(
+		x,
+		y,
+		x + w - 1,
+		y + h - 1
+	);
+
+	uint16_t num = w * h;
+	while (num--)
+		pulse_draw_point24(color);
+}
+
+/** \file
+ * Line and pixel drawing routines.
+ * Using a virtual screen buffer that is twice the resolution
+ * along each axis so that anti-aliasing can be done later.
+ */
+#ifndef _draw_h_
+
+#include <pulse_os.h>
+#include <pulse_types.h>
+#include "app_resources.h"
+
+#define VSCREEN_SHIFT	4
+#define VSCREEN_PIXEL	(1 << VSCREEN_SHIFT)
+#define VSCREEN_MASK	(VSCREEN_PIXEL - 1)
+#define VSCREEN_WIDTH	(((int32_t) SCREEN_WIDTH) << VSCREEN_SHIFT)
+#define VSCREEN_HEIGHT	(((int32_t) SCREEN_HEIGHT) << VSCREEN_SHIFT)
+
+/** Predefined colors that we use */
+#define COLOR_GRAY		((color24_t) { 0x18, 0x18, 0x18, 0 } )
+#define COLOR_RED		((color24_t) { 0xFF, 0x00, 0x00, 0 } )
+#define COLOR_GREEN		((color24_t) { 0x00, 0xFF, 0x00, 0 } )
+#define COLOR_DARKGREEN		((color24_t) { 0x00, 0x10, 0x00, 0 } )
+#define COLOR_YELLOW		((color24_t) { 0xF0, 0xF0, 0x00, 0 } )
+#define COLOR_MIDNIGHTBLUE	((color24_t) { 0x19, 0x19, 0x70, 0 } )
+#define COLOR_BLUE		((color24_t) { 0x00, 0x00, 0xC0, 0 } )
+#define COLOR_WHITE		((color24_t) { 0xFF, 0xFF, 0xFF, 0 } )
+#define COLOR_BLACK		((color24_t) { 0x00, 0x00, 0x00, 0 } )
+
+
+/* Draw a pixel in vscreen coordinates */
+void
+draw_pixel(
+	color24_t c,
+	int32_t x,
+	int32_t y
+);
+
+
+void
+fill(
+	color24_t color,
+	int32_t x0,
+	int32_t y0,
+	int32_t x1,
+	int32_t y1
+);
+
+
+/* Draw a non-antialised line using Bresenham's_line_algorithm
+ * in vscreen coordinates */
+void
+draw_line(
+	color24_t c,
+	int32_t x0,
+	int32_t y0,
+	int32_t x1,
+	int32_t y1
+);
+
+
+void
+draw_monostring(
+	int32_t x,
+	int32_t y,
+	color24_t color,
+	PulseResource font,
+	int width,
+	const char * s
+);
+
+#endif

fireworks/fireworks.c

+/** \file
+ * Draw the digits exploding into place.
+ */
+#include <string.h>
+#include "main.h"
+#include "draw.h"
+#include "sin_table.h"
+#include "hershey.h"
+
+
+typedef struct {
+	uint8_t value;
+	int32_t scale;
+	int32_t x;
+	int32_t y;
+} digit_t;
+
+static digit_t last_sec, last_min, last_hour;
+
+void
+init(void)
+{
+	last_sec.value = last_min.value = last_hour.value = -1;
+}
+
+
+void
+button_down(void)
+{
+	init();
+}
+
+
+void
+button_up(
+	uint32_t ms
+)
+{
+	// Nothing to do
+	(void) ms;
+}
+
+/** Center the display at the bottom of the screen in the middle
+ * of the row.
+ */
+static const int32_t cx = VSCREEN_WIDTH/2;
+static const int32_t cy = VSCREEN_HEIGHT;
+
+
+/** Draw digits, after translating and then rotating around (cx,cy) */
+static void
+draw_digit(
+	color24_t color,
+	const uint16_t angle,
+	int32_t scale,
+	char c,
+	const int32_t x0,
+	const int32_t y0
+)
+{
+	if ('0' <= c && c <= '9')
+		c -= '0';
+	else
+		return;
+
+	const int32_t rot_sin = sin_lookup(angle);
+	const int32_t rot_cos = cos_lookup(angle);
+
+	const path_t * d = digits[(uint8_t) c];
+	int32_t x1 = 0;
+	int32_t y1 = 0;
+	int32_t px1 = 0;
+	int32_t py1 = 0;
+	int8_t last_x = -1;
+	int8_t last_y = -1;
+
+	while (1)
+	{
+		int32_t x = d->y;
+		int32_t y = d->x;
+		d++;
+
+		if (x == 0 && y == 0)
+			break;
+		if (x == -1 && y == -1)
+		{
+			// Pen up
+			last_x = last_y = -1;
+			continue;
+		}
+
+		int32_t x2 = x0 + (scale * x) / 12;
+		int32_t y2 = y0 + (scale * y) / 12;
+
+		// Rotate the coordinates by the matrix and into screen frame
+		int32_t py2 = (x2 * rot_sin + y2 * rot_cos) / 32768
+			+ cy;
+		int32_t px2 = (x2 * rot_cos - y2 * rot_sin) / 32768
+			+ cx;
+
+		if (last_x != -1 && last_y != -1
+		&&  0 <= px1 && px1 < VSCREEN_WIDTH
+		&&  0 <= px2 && px2 < VSCREEN_WIDTH
+		&&  0 <= py1 && py1 < VSCREEN_HEIGHT
+		&&  0 <= py2 && py2 < VSCREEN_HEIGHT)
+			draw_line(color, px1, py1, px2, py2);
+
+		x1 = x2;
+		y1 = y2;
+		px1 = px2;
+		py1 = py2;
+		last_x = x;
+		last_y = y;
+	}
+}
+
+
+static void
+draw_digit_string(
+	color24_t color,
+	const uint16_t angle,
+	int32_t scale,
+	const char * s,
+	const int32_t x0,
+	const int32_t y0
+)
+{
+	char c;
+	int32_t y = y0;
+	while ((c = *s++))
+	{
+		draw_digit(color, angle, scale, c, x0, y);
+		y += scale + (3 << VSCREEN_SHIFT);
+	}
+}
+
+
+
+static const char digit_strings[][3] = {
+	"00", "01", "02", "03", "04", "05", "06", "07", "08", "09",
+	"10", "11", "12", "13", "14", "15", "16", "17", "18", "19",
+	"20", "21", "22", "23", "24", "25", "26", "27", "28", "29",
+	"30", "31", "32", "33", "34", "35", "36", "37", "38", "39",
+	"40", "41", "42", "43", "44", "45", "46", "47", "48", "49",
+	"50", "51", "52", "53", "54", "55", "56", "57", "58", "59",
+};
+
+
+// Draw a zooming upwards digit
+static void
+draw_time(
+	digit_t * last,
+	uint8_t value,
+	uint32_t ms
+)
+{
+	int32_t scale = (ms * (10 << VSCREEN_SHIFT)) / 1000;
+	int32_t y = (ms * VSCREEN_HEIGHT) / 1000;
+	int32_t x = (ms * (20 << VSCREEN_SHIFT)) / 1000;
+	const char * s = digit_strings[value];
+	color24_t color = {
+		(256 * ms) / 1000,
+		(256 * ms) / 1000,
+		(256 * ms) / 1000
+	};
+
+	if (last->value != -1)
+	{
+		// Erase the previous drawing
+		draw_digit_string(
+			COLOR_BLACK,
+			-16384,
+			last->scale,
+			digit_strings[last->value],
+			last->y,
+			last->x
+		);
+	}
+
+	last->value = value;
+	last->x = x;
+	last->y = y;
+	last->scale = scale;
+
+
+	draw_digit_string(
+		color,
+		-16384,
+		scale,
+		s,
+		y,
+		x
+	);
+}
+
+
+void
+draw(
+	uint32_t cur_ms
+)
+{
+	uint32_t ms = cur_ms % 1000; cur_ms /= 1000;
+	const uint32_t sec = cur_ms % 60; cur_ms /= 60;
+	const uint8_t min = cur_ms % 60; cur_ms /= 60;
+	const uint8_t hour = cur_ms %= 24;
+
+	draw_time(&last_sec, sec, ms);
+
+
+/*
+	draw_digit_string(
+		COLOR_WHITE,
+		-16384,
+		scale,
+		digit_strings[hour],
+		0,
+		-4 * scale
+	);
+
+	draw_digit_string(
+		COLOR_WHITE,
+		-16384,
+		scale,
+		digit_strings[min],
+		0,
+		-1 * scale
+	);
+*/
+}

fireworks/hershey.c

+/** \file
+ * Hershey fonts for the numbers 0-9
+ * From:
+ * http://paulbourke.net/dataformats/hershey/
+ *
+ * Point x,y == -1 => pen up
+ * Otherwise draw in a continuous path
+ */
+
+#include "hershey.h"
+
+#define POINT(x,y) { x, y }
+
+const path_t digits[][32] = {
+	[0] = {
+		POINT( 9,21),
+		POINT( 6,20),
+		POINT( 4,17),
+		POINT( 3,12),
+		POINT( 3, 9),
+		POINT( 4, 4),
+		POINT( 6, 1),
+		POINT( 9, 0),
+		POINT(11, 0),
+		POINT(14, 1),
+		POINT(16, 4),
+		POINT(17, 9),
+		POINT(17,12),
+		POINT(16,17),
+		POINT(14,20),
+		POINT(11,21),
+		POINT( 9,21),
+	},
+	[1] = {
+		POINT( 6,17),
+		POINT( 8,18),
+		POINT(11,21),
+		POINT(11, 0),
+	},
+	[2] = {
+		POINT( 4,16),
+		POINT( 4,17),
+		POINT( 5,19),
+		POINT( 6,20),
+		POINT( 8,21),
+		POINT(12,21),
+		POINT(14,20),
+		POINT(15,19),
+		POINT(16,17),
+		POINT(16,15),
+		POINT(15,13),
+		POINT(13,10),
+		POINT( 3, 0),
+		POINT(17, 0),
+	},
+	[3] = {
+		POINT( 5,21),
+		POINT(16,21),
+		POINT(10,13),
+		POINT(13,13),
+		POINT(15,12),
+		POINT(16,11),
+		POINT(17, 8),
+		POINT(17, 6),
+		POINT(16, 3),
+		POINT(14, 1),
+		POINT(11, 0),
+		POINT( 8, 0),
+		POINT( 5, 1),
+		POINT( 4, 2),
+		POINT( 3, 4),
+	},
+	[4] = {
+		POINT(13,21),
+		POINT( 3, 7),
+		POINT(18, 7),
+		POINT(-1,-1),
+		POINT(13,21),
+		POINT(13, 0),
+	},
+	[5] = {
+		POINT(15,21),
+		POINT( 5,21),
+		POINT( 4,12),
+		POINT( 5,13),
+		POINT( 8,14),
+		POINT(11,14),
+		POINT(14,13),
+		POINT(16,11),
+		POINT(17, 8),
+		POINT(17, 6),
+		POINT(16, 3),
+		POINT(14, 1),
+		POINT(11, 0),
+		POINT( 8, 0),
+		POINT( 5, 1),
+		POINT( 4, 2),
+		POINT( 3, 4),
+	},
+	[6] = {
+		POINT(16,18),
+		POINT(15,20),
+		POINT(12,21),
+		POINT(10,21),
+		POINT( 7,20),
+		POINT( 5,17),
+		POINT( 4,12),
+		POINT( 4, 7),
+		POINT( 5, 3),
+		POINT( 7, 1),
+		POINT(10, 0),
+		POINT(11, 0),
+		POINT(14, 1),
+		POINT(16, 3),
+		POINT(17, 6),
+		POINT(17, 7),
+		POINT(16,10),
+		POINT(14,12),
+		POINT(11,13),
+		POINT(10,13),
+		POINT( 7,12),
+		POINT( 5,10),
+		POINT( 4, 7),
+	},
+	[7] = {
+		POINT(17,21),
+		POINT( 7, 0),
+		POINT(-1,-1),
+		POINT( 3,21),
+		POINT(17,21),
+	},
+	[8] = {
+		POINT( 8,21),
+		POINT( 5,20),
+		POINT( 4,18),
+		POINT( 4,16),
+		POINT( 5,14),
+		POINT( 7,13),
+		POINT(11,12),
+		POINT(14,11),
+		POINT(16, 9),
+		POINT(17, 7),
+		POINT(17, 4),
+		POINT(16, 2),
+		POINT(15, 1),
+		POINT(12, 0),
+		POINT( 8, 0),
+		POINT( 5, 1),
+		POINT( 4, 2),
+		POINT( 3, 4),
+		POINT( 3, 7),
+		POINT( 4, 9),
+		POINT( 6,11),
+		POINT( 9,12),
+		POINT(13,13),
+		POINT(15,14),
+		POINT(16,16),
+		POINT(16,18),
+		POINT(15,20),
+		POINT(12,21),
+		POINT( 8,21),
+	},
+	[9] = {
+		POINT(16,14),
+		POINT(15,11),
+		POINT(13, 9),
+		POINT(10, 8),
+		POINT( 9, 8),
+		POINT( 6, 9),
+		POINT( 4,11),
+		POINT( 3,14),
+		POINT( 3,15),
+		POINT( 4,18),
+		POINT( 6,20),
+		POINT( 9,21),
+		POINT(10,21),
+		POINT(13,20),
+		POINT(15,18),
+		POINT(16,14),
+		POINT(16, 9),
+		POINT(15, 4),
+		POINT(13, 1),
+		POINT(10, 0),
+		POINT( 8, 0),
+		POINT( 5, 1),
+		POINT( 4, 3),
+	},
+};

fireworks/hershey.h

+#ifndef _hershey_h_
+#define _hershey_h_
+
+#include <stdint.h>
+
+typedef struct
+{
+	int8_t x;
+	int8_t y;
+} __attribute__((__packed__))
+path_t;
+
+extern const path_t digits[][32];
+
+#endif
+/** \file
+ * Main program for all of the 30-days watches.
+ *
+ * The mundane bits involved with power management, setup, etc are
+ * handled here.  The bootloader mode is also done here.
+ *
+ * The watch program only needs to implement:
+ *
+ * init (called by wakeup)
+ * draw
+ *
+ */
+#include "main.h"
+#include "draw.h"
+
+
+static uint32_t not_charging;
+static uint8_t shutdown_scheduled;
+
+
+/** At wake up, sample the boot time in ms and use it to scale
+ * the ms timestamp to a time since midnight
+ */
+static uint32_t ms_since_midnight;
+static uint32_t epoch_ms;
+
+/* Track how long the button has been held down for */
+static uint8_t do_load;
+static uint32_t button_down_ms;
+
+
+
+
+static void
+do_empty_loop(void)
+{
+	static uint8_t last_sec;
+	struct pulse_time_tm now;
+	pulse_get_time_date(&now);
+	if (now.tm_sec == last_sec)
+		return;
+	last_sec = now.tm_sec;
+
+	printf("%02d:%02d:%02d\n", now.tm_hour, now.tm_min, now.tm_sec);
+}
+
+
+
+void
+main_app_loop(void)
+{
+	// Limit the update rate to 30 fps == 33 ms/frame
+	const uint64_t millis = pulse_get_millis();
+	if (button_down_ms && millis - button_down_ms > 1000)
+	{
+		do_load = !do_load;
+		button_down_ms = 0;
+		return;
+	}
+	if (do_load)
+	{
+		do_empty_loop();
+		return;
+	}
+
+	// Draw the charging indicator
+	color24_t charge_light = not_charging
+		? (color24_t){10,10,10}
+		: (color24_t){0x80,0,0};
+
+	fill(
+		charge_light,
+		0,
+		8,
+		2,
+		12
+	);
+
+        // If the not-charging state has not been toggled in the last
+        // 30 seconds, schedule a power down soon.
+        if (not_charging && millis - not_charging > 60000)
+	{
+		if (!shutdown_scheduled)
+			pulse_update_power_down_timer(1);
+		shutdown_scheduled = 1;
+		return;
+	}
+
+	// Compute the ms since midnight
+	uint32_t cur_ms = ms_since_midnight + (millis - epoch_ms);
+	draw(cur_ms);
+}
+
+
+static void
+wakeup(void *arg)
+{
+	pulse_oled_set_brightness(100);
+	pulse_blank_canvas();
+	not_charging = pulse_get_millis();
+	shutdown_scheduled = 0;
+
+	// Compute the number of mseconds since midnight
+	struct pulse_time_tm now;
+	pulse_get_time_date(&now);
+	uint32_t sec_since_midnight = 0
+		+ now.tm_sec
+		+ now.tm_min * 60
+		+ now.tm_hour * 3600;
+	ms_since_midnight = sec_since_midnight * 1000;
+	epoch_ms = pulse_get_millis();
+	do_load = 0;
+	button_down_ms = 0;
+
+	init();
+}
+
+
+void
+main_app_init(void)
+{
+	// Register a callback for the woke_from_button event
+	pulse_register_callback(
+		ACTION_WOKE_FROM_BUTTON,
+		&wakeup
+	);
+
+	wakeup(0);
+}
+
+
+void
+main_app_handle_button_down(void)
+{
+	// Track when the button was pressed and draw a small
+	// marker to indicate it since my button is twitchy.
+	button_down_ms = pulse_get_millis();
+
+	// Reset the shutdown timer when they hit the button
+	if (not_charging)
+		not_charging = pulse_get_millis();
+
+	fill(
+		COLOR_BLUE,
+		VSCREEN_WIDTH - (2 << VSCREEN_SHIFT),
+		VSCREEN_HEIGHT/2 - (2 << VSCREEN_SHIFT),
+		VSCREEN_WIDTH - 1,
+		VSCREEN_HEIGHT/2 + (2 << VSCREEN_SHIFT)
+	);
+
+	button_down();
+}
+
+
+void
+main_app_handle_button_up(void)
+{
+	const uint32_t now = pulse_get_millis();
+
+	// Stop the power down timer
+	if (not_charging)
+		not_charging = now;
+
+	button_up(now - button_down_ms);
+	button_down_ms = 0;
+
+	fill(
+		COLOR_BLACK,
+		VSCREEN_WIDTH - (2 << VSCREEN_SHIFT),
+		VSCREEN_HEIGHT/2 - (2 << VSCREEN_SHIFT),
+		VSCREEN_WIDTH - 1,
+		VSCREEN_HEIGHT/2 + (2 << VSCREEN_SHIFT)
+	);
+
+	
+}
+
+
+void
+main_app_handle_hardware_update(
+	enum PulseHardwareEvent event
+)
+{
+	switch (event)
+	{
+	case BATTERY_CHARGING:
+		// Erase the last non-charging time to avoid flakey power
+		not_charging = 0;
+		break;
+
+	case BATTERY_NOT_CHARGING:
+		// Record the time that the state was toggled.
+		not_charging = pulse_get_millis();
+		break;
+
+	default:
+		break;
+	}
+}
+
+
+void
+main_app_handle_doz(void)
+{
+	// Fade out
+	for (int i = 100; i >= 0; i -= 6)
+	{
+		pulse_oled_set_brightness(i);
+		pulse_mdelay(60);
+	}
+}
+/** \file
+ * Simplified watch interface.
+ *
+ * The watch program must implement these functions.
+ */
+#ifndef _main_h_
+#define _main_h_
+
+#include <pulse_os.h>
+#include <pulse_types.h>
+#include <app_resources.h>
+#include <stdio.h>
+#include <stdint.h>
+
+
+/** init() is called when the watch wakes up.
+ * The screen will be blank and the OLED will be at 100%.
+ */
+void init(void);
+
+/** draw() is called everytime the app mainloop is invoked.
+ * \todo Frame rate limiting?
+ */
+void draw(uint32_t ms_since_midnight);
+
+/** button_down() is called as soon as the button is pressed */
+void button_down(void);
+
+/** button_up() is called as soon as the button is released
+ * with the length of time in ms that the button was held.
+ */
+void button_up(uint32_t ms_held);
+
+#endif

fireworks/resources/fonts.txt

+# This file describes font rendering options
+# 
+# <filename>, <friendly_name>, <px_size>, <clock_font>, <pixel_font>
+#
+# <filename>      - filename of the ttf font to render 
+#                   ('#' is an illegal character in the name and avoid spaces in filenames)
+# <friendly_name> - name that will be used in the header file generated
+#                   ('#' and spaces are illegal characters in the name)
+# <px_size>       - size in px to render the font
+# <clock_font>    - (y/n) only render characters 0 - 9, :, a, p, A, P (for use as a clock)
+# <pixel_font>    - (y/n) don't render grayscale (for pixel fonts)
+#
+# Example:
+# good_times.ttf, good_times_10, 10, n, n
+#Clockopia.ttf, clockopia_32, 32, y, n
+#Clockopia.ttf, clockopia_22, 22, y, n

fireworks/sin_table.c

+#include "sin_table.h"
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+
+static const uint8_t sin_table[] = {
+	0,
+	25,
+	50,
+	74,
+	98,
+	121,
+	142,
+	162,
+	181,
+	198,
+	213,
+	226,
+	237,
+	245,
+	251,
+	255,
+	255 // duplicate to avoid extra code for handling last case
+};
+
+
+int16_t
+sin_lookup(
+	uint16_t theta_in
+)
+{
+	int sign = 1;
+	uint32_t theta = theta_in;
+
+	if (theta < 64ul*256)
+	{
+		// q1, upward slope, normal theta, positive sign
+	} else
+	if (theta < 128ul*256)
+	{
+		theta = 128ul*256 - theta; // downward slope, still positive
+	} else
+	if (theta < 192ul*256)
+	{
+		// q3, downward slope, negative side
+		theta = theta - 128ul*256;
+		sign = -1;
+	} else
+	{
+		// q4, upward slope, negative side
+		theta = 256ul*256 - theta;
+		sign = -1;
+	}
+
+	int32_t s1 = ((uint32_t) sin_table[(theta >> 10) + 0]) << 8;
+	int32_t s2 = ((uint32_t) sin_table[(theta >> 10) + 1]) << 8;
+	int16_t result = (s1 + ((s2 - s1) * (theta & 1023)) / 1024) / 2;
+
+	if (sign == -1)
+		return -result;
+	else
+		return result;
+}
+
+
+#if 0
+int main(void)
+{
+	unsigned theta;
+
+	for (theta = 0 ; theta < 0x100 ; theta++)
+	{
+		double sx = sin_lookup(theta) / 128.0;
+		double sf = sin(theta * 2 * M_PI / 256.0);
+		double cx = cos_lookup(theta) / 128.0;
+		double cf = cos(theta * 2 * M_PI / 256.0);
+
+		printf("%.4f %.4f %.4f\n",
+			cx,
+			cf,
+			cf - cx
+		);
+	}
+}
+#endif

fireworks/sin_table.h

+/** \file
+ * Approximate sin function.
+ * theta goes from 0 == 0 Pi to 65536 == 2 Pi
+ */
+#ifndef _sin_table_h_
+#define _sin_table_h_
+
+#include <stdint.h>
+
+extern int16_t sin_lookup(uint16_t theta);
+
+static inline int16_t
+cos_lookup(
+	uint16_t theta
+)
+{
+	return sin_lookup(theta + 16384);
+}
+
+
+#endif
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.