Commit 6bcb071c authored by Øyvind Kolås's avatar Øyvind Kolås
Browse files

(feat/doc:ctx) add a l0dable demonstrating ui and rendering with ctx

parent 0ee889e2
CFLAGS+=-DCTX_SIMULATOR -DCARD10_CTX_STATIC -lm -Wall -I../../lib/ctx
LD_FLAGS+=-lm
ctx-demo: ctx-demo.o ui.o Makefile ../../lib/ctx/ctx.h
$(CC) -s -Os -o $@ ctx-demo.o ui.o $(LD_FLAGS)
ctx-demo.o: ctx-demo.c ui.h Makefile
$(CC) -c -Os -o $@ $< $(CFLAGS)
ui.o: ui.c ui.h Makefile
$(CC) -c -Os -o $@ $< $(CFLAGS)
test: ctx-demo
./ctx-demo
clean:
rm -f ctx-demo *.o
/*
* Copyright 2019-2020 Øyvind Kolås <pippin@gimp.org>
*/
#ifndef _DEFAULT_SOURCE
#define _DEFAULT_SOURCE
#endif
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <unistd.h>
#include <math.h>
#include "ctx.h"
#include "ui.h"
extern UiItem ctx_demo[];
#if CARD10_CTX_STATIC
#ifndef CTX_SIMULATOR
static uint16_t pixels[DISP_WIDTH * DISP_HEIGHT];
#endif
#endif
int main(int argc, char *argv[])
{
int quit = 0;
#if CARD10_CTX_STATIC
#if CTX_SIMULATOR
Ctx *ctx = ctx_new_ui (DISP_WIDTH, DISP_HEIGHT);
#else
Ctx *ctx = ctx_new_for_framebuffer (pixels, DISP_WIDTH, DISP_HEIGHT,
DISP_WIDTH * 2,
CTX_FORMAT_RGB565_BYTESWAPPED);
ctx_translate (ctx, DISP_WIDTH, DISP_HEIGHT);
ctx_scale (ctx, -1.0f, -1.0f);
#endif
#else
Ctx *ctx = ctx_new ();
ctx_set_size (ctx, DISP_WIDTH, DISP_HEIGHT); // for parser ui_items
#endif
ui_set_items (ctx_demo);
while (!quit)
{
#if CARD10_CTX_STATIC
// why can't we take a ctx_reset here? (it causes mostly blank render,
// not only transform going to identity XXX XXX
#ifdef CTX_SIMULATOR
ctx_reset (ctx);
#endif
#else
ctx_reset (ctx);
#endif
ctx_rectangle (ctx, 0, 0, DISP_WIDTH, DISP_HEIGHT);
ctx_rgba8 (ctx, 0,0,0,255);
ctx_fill (ctx);
ctx_save (ctx);
ui_item_draw_fullscreen (ctx, ui_current_scene());
ctx_restore (ctx);
#ifdef CTX_SIMULATOR
ctx_flush (ctx);
usleep (20 * 1000);
#else
epic_disp_open ();
#if CARD10_CTX_STATIC
epic_disp_framebuffer ((void*)pixels);
#else
epic_disp_ctx ((void*)ctx_get_renderstream (ctx),
ctx_get_renderstream_count (ctx) * 9);
epic_disp_update ();
#endif
epic_disp_close ();
#endif
ui_event_update (ctx);
frame_no ++;
if (ui_event_match (BUTTON_LEFT_TOP, UI_PRESS))
quit = 1;
}
return 0;
}
static void scope (Ctx *ctx, int frame_no)
{
ctx_rgba8 (ctx, 255,0,0,255);
ctx_line_width (ctx, -1);
for (int i = 0; i < 180; i++)
{
float x = i;
float y = DISP_HEIGHT*0.5f*sinf ((x+frame_no) / 10.0)+DISP_HEIGHT/2;
if (i == 0)
ctx_move_to (ctx, x, y);
else
ctx_line_to (ctx, x, y);
}
ctx_stroke (ctx);
}
static void scope2 (Ctx *ctx, int frame_no)
{
ctx_rgba8 (ctx, 255,0,0,255);
ctx_line_width (ctx, -1);
for (int i = 0; i < 180; i++)
{
float x = i;
float y = DISP_HEIGHT / 2 *
sinf ((x+frame_no)/7.0f) * cosf((x+frame_no)/3.0f) + DISP_HEIGHT/2;
if (i == 0)
ctx_move_to (ctx, x, y);
else
ctx_line_to (ctx, x, y);
}
ctx_stroke (ctx);
}
static void _analog_clock (Ctx *ctx, int frame_no, int smoothstep)
{
uint64_t ms64 = ui_ticks;
uint32_t ms = ms64;
uint32_t s = ms / 1000;
uint32_t m = s / 60;
uint32_t h = m / 60;
ms = ((uint32_t)(ms))%1000;
s %= 60;
m %= 60;
h %= 12;
float r = frame_no * 0.008;
ctx_save (ctx);
ctx_rgba8 (ctx, 127,127,127,255);
ctx_move_to (ctx, DISP_WIDTH/2 + DISP_HEIGHT * 0.45, DISP_HEIGHT/2);
ctx_arc (ctx, DISP_WIDTH/2, DISP_HEIGHT/2, DISP_HEIGHT * 0.45, 0.0, CTX_PI * 2, 0);
ctx_line_width (ctx, 8);
ctx_line_cap (ctx, CTX_CAP_NONE);
ctx_stroke (ctx);
ctx_line_width (ctx, 7);
ctx_line_cap (ctx, CTX_CAP_ROUND);
ctx_rgba8 (ctx, 188,188,188,255);
r = m * CTX_PI * 2/ 60;
ctx_move_to (ctx, DISP_WIDTH/2, DISP_HEIGHT/2);
ctx_line_to (ctx, DISP_WIDTH/2 + cosf(r) * DISP_HEIGHT * 0.32f, DISP_HEIGHT/2 + sinf (r) * DISP_HEIGHT * 0.32f);
ctx_line_to (ctx, DISP_WIDTH/2 + cosf(r) * DISP_HEIGHT * 0.33f, DISP_HEIGHT/2 + sinf (r) * DISP_HEIGHT * 0.33f);
ctx_stroke (ctx);
r = (h + m/60.0f) * CTX_PI * 2/ 12;
ctx_move_to (ctx, DISP_WIDTH/2, DISP_HEIGHT/2);
ctx_line_to (ctx, DISP_WIDTH/2 + cosf(r) * DISP_HEIGHT * 0.25f, DISP_HEIGHT/2 + sinf (r) * DISP_HEIGHT * 0.25f);
ctx_line_to (ctx, DISP_WIDTH/2 + cosf(r) * DISP_HEIGHT * 0.26f, DISP_HEIGHT/2 + sinf (r) * DISP_HEIGHT * 0.26f);
ctx_stroke (ctx);
ctx_line_width (ctx, 4);
ctx_line_cap (ctx, CTX_CAP_NONE);
ctx_rgba8 (ctx, 255,0,0,127);
if (smoothstep)
r = (s + ms/1000.0f) * CTX_PI * 2/ 60;
else
r = (s ) * CTX_PI * 2/ 60;
ctx_move_to (ctx, DISP_WIDTH/2, DISP_HEIGHT/2);
ctx_line_to (ctx, DISP_WIDTH/2 + cosf(r) * DISP_HEIGHT * 0.45f, DISP_HEIGHT/2 + sinf (r) * DISP_HEIGHT * 0.45f);
ctx_line_to (ctx, DISP_WIDTH/2 + cosf(r) * DISP_HEIGHT * 0.46f, DISP_HEIGHT/2 + sinf (r) * DISP_HEIGHT * 0.46f);
ctx_stroke (ctx);
ctx_restore (ctx);
}
static void analog_clock (Ctx *ctx, int frame_no)
{
_analog_clock (ctx, frame_no, 1);
}
static void gradient_text (Ctx *ctx, int frame_no)
{
frame_no = frame_no % 400;
float r = frame_no * 0.01;
ctx_save (ctx);
//ctx_rgba8 (ctx, 255,255,255,255);
ctx_linear_gradient (ctx, 12, 12, 200, 200);
ctx_translate (ctx, DISP_WIDTH/2, DISP_HEIGHT/2);
ctx_rotate (ctx, r * 1.66);
ctx_translate (ctx, -DISP_WIDTH/2, -DISP_HEIGHT/2);
ctx_move_to (ctx, 0, 40);
ctx_font_size (ctx, 24);
ctx_text (ctx, "card10+ctx");
ctx_restore (ctx);
}
static void spin_text (Ctx *ctx, int frame_no)
{
UiItem *item = ui_current_scene ();
const char *string = "card10+ctx";
if (item->data) string = item->data;
frame_no = frame_no % 400;
float r = frame_no * 0.01;
ctx_save (ctx);
ctx_rgba8 (ctx, 255,255,255,255);
ctx_translate (ctx, DISP_WIDTH/2, DISP_HEIGHT/2);
ctx_rotate (ctx, r * 1.66);
ctx_translate (ctx, -DISP_WIDTH/2, -DISP_HEIGHT/2);
ctx_move_to (ctx, 0, 40);
ctx_font_size (ctx, 24);
ctx_text (ctx, string);
ctx_restore (ctx);
}
static void counter (Ctx *ctx, int frame_no)
{
UiItem *item = ui_current_scene ();
int timeout = item->arg1 * 1000;
//float r = (ui_ticks-ui_scene_timer)*1.0f/timeout;
float t = (ui_ticks-ui_scene_timer)/1000.0;
char buf[64];
ctx_rgba8 (ctx, 255,255,255,255);
ctx_move_to (ctx, 5, 30);
ctx_font_size (ctx, 14.0);
sprintf (buf, "%i%% %i/%i %i", (ui_ticks-ui_scene_timer)*100/timeout,
ui_ticks-ui_scene_timer, timeout, (int)(t*10));
ctx_text (ctx, buf);
}
static void font_scaling (Ctx *ctx, int frame_no)
{
char buf[64];
ctx_rgba8 (ctx, 255,255,255,255);
ctx_move_to (ctx, 5, 40 - (frame_no % 50));
ctx_font_size (ctx, ui_font_size);
ctx_text (ctx, "Press top right to change\nfont size, press lower right\nto advance to next test.\nThis text is moving\none pixel per frame.");
ctx_move_to (ctx, 0, 15);
ctx_font_size (ctx, 12.0);
sprintf (buf, "size: %i", (int)ui_font_size);
ctx_text (ctx, buf);
if (ui_event_match (BUTTON_RIGHT_TOP, UI_PRESS_REPEAT))
{
ui_font_size += 1;
if (ui_font_size > 40.0f) ui_font_size = 4.0f;
}
}
static void circle_spiral (Ctx *ctx, int frame_no)
{
UiItem *item = ui_current_scene ();
int timeout = item->arg1 * 1000;
float r = (ui_ticks-ui_scene_timer)*1.0f/timeout;
//float t = (ui_ticks-ui_scene_timer)/1000.0;
static int dot_count = 2;
static float twist = 2.9645;
static float dot_scale = 52.0;
if (frame_no == 0)
{
dot_count = 2;
twist = 2.9645;
dot_scale = 52.0;
}
twist = 2.9645 * (1.0 -r) + r * 3.0;
dot_count = 2.0 * (1.0 - r) + r * 256;
ctx_rgba(ctx, 1, 1, 1, 0.5);
for (int i = 0; i < dot_count; i ++)
{
float x = DISP_WIDTH / 2;
float y = DISP_HEIGHT / 2;
float radius = DISP_HEIGHT / dot_scale;
float dist = i * (DISP_HEIGHT/ 2) / (dot_count * 1.0f);
float twisted = (i * twist);
float cos_twisted = cosf (twisted);
float sin_twisted = sinf (twisted);
x += cos_twisted * dist;
y += sin_twisted * dist;
ctx_arc (ctx, x, y, radius, 0, CTX_PI * 2.1, 0);
ctx_fill (ctx);
}
#if 0
itk_panel_start (itk, "spiraling dots", ctx_width(ctx)*3/4,0,ctx_width(ctx)/4, ctx_height(ctx)/3);
itk_slider_int (itk, "count", &dot_count, 1, 4000, 10);
itk_slider_float (itk, "radius", &dot_scale, 2.0, 200.0, 4.5);
itk_slider_float (itk, "twist amount", &twist, -3.14152, 3.14152, 0.0005);
#endif
}
static void parser (Ctx *ctx, int frame_no)
{
UiItem *item = ui_current_scene ();
if (item->data)
{
ctx_parse (ctx, item->data);
}
else
{
}
}
static void launch (Ctx *ctx, void *user_data)
{
ui_autoplay = 1;
ui_ui_autoplay_timeout = 4000;
ui_title_timeout = 2000;
//ui_font_size = 19;
ui_no = 7;
}
extern int ui_long_press_delay;
extern int ui_press_repeat_delay;
UiItem ctx_demo[]={
//{"counter", ui_scene, counter, "ctx", 2.0},
//{"counter", ui_scene, counter, "ctx", 4.0},
{"spin text", ui_scene, spin_text, "ctx", 1.0},
//{"counter", ui_scene, counter, "ctx", 1.0},
// {"spin text", ui_scene, spin_text, "foo", 1.0},
// {"spin text", ui_scene, spin_text, "bar", 2.0},
{"ctx demo", ui_menu},
{"launch", ui_button, launch, NULL},
{"setup", ui_sub_menu},
{"", ui_separator},
{"analog clock", ui_scene, analog_clock},
{"circle spiral", ui_scene, circle_spiral, NULL, 10.0},
{"sine", ui_scene, scope},
{"gradient text", ui_scene, gradient_text},
{"complex signal", ui_scene, scope2},
{"parser", ui_scene, parser, "rgb 0 0 1 rectangle 4 4 4 4 fill "},
{"parser", ui_scene, parser,
"rgb 1 0 0 rectangle 10, 10, 40, 40 fill "
"rgb 1 1 0 ui_font_size 20 m 15 30x 'hello' "},
{"long string", ui_scene, font_scaling},
{"spin text", ui_scene, spin_text},
{"setup", ui_menu},
{"autoplay", ui_boolean_toggle, &ui_autoplay},
{"autoplay timeout", ui_int_slider, &ui_ui_autoplay_timeout, NULL, 1, 20000, 500},
{"title timeout ms", ui_int_slider, &ui_title_timeout, NULL, 1, 20000, 500},
{"", ui_separator},
{"font size", ui_float_slider, &ui_font_size, NULL, 4.0, 50.0, 1},
{"line height", ui_float_slider, &ui_line_height, NULL, 0.5, 2.0, 0.05},
{"scroll jump", ui_float_slider, &ui_scroll_jump, NULL, 0.0, 1.0, 0.05},
{"long press ms", ui_int_slider, &ui_long_press_delay, NULL, 10, 1000, 10},
{"press repeat ms", ui_int_slider, &ui_press_repeat_delay, NULL, 10, 1000, 10},
{NULL, NULL},
};
name = 'ctx-demo'
elf = executable(
name + '.elf',
['ctx-demo.c',
'ui.c',
'ui.h'
],
build_by_default: true,
dependencies: [l0dable_startup, api_caller],
link_whole: [l0dable_startup_lib],
link_args: [
'-s', '-Wl,-Map=' + meson.current_build_dir() + '/' + name + '.map'
],
pie: true,
c_args: [ '-Wno-unused-function',
'-Wno-missing-field-initializers',
'-I../lib/ctx',
'-Os',
'-s', '-fomit-frame-pointer' ]
)
elf = executable(
name + '-static.elf',
['ctx-demo.c',
'ui.c',
'ui.h'
],
build_by_default: true,
dependencies: [l0dable_startup, api_caller],
link_whole: [l0dable_startup_lib],
link_args: [
'-s', '-Wl,-Map=' + meson.current_build_dir() + '/' + name + '.map'
],
pie: true,
c_args: [ '-Wno-unused-function',
'-Wno-missing-field-initializers',
'-I../lib/ctx',
'-O3',
'-DCARD10_CTX_STATIC=1', '-s', '-fomit-frame-pointer' ]
)
This diff is collapsed.
#ifndef __UI_H_
#define __UI_H_
/*
* Copyright 2019-2020 Øyvind Kolås <pippin@gimp.org>
*/
#ifndef _DEFAULT_SOURCE
#define _DEFAULT_SOURCE
#endif
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#if CTX_SIMULATOR
#define DISP_WIDTH 160
#define DISP_HEIGHT 80
enum epic_button {
/** ``1``, Bottom left button (bit 0). */
BUTTON_LEFT_BOTTOM = 1,
/** ``2``, Bottom right button (bit 1). */
BUTTON_RIGHT_BOTTOM = 2,
/** ``4``, Top right button (bit 2). */
BUTTON_RIGHT_TOP = 4,
/** ``8``, Top left (power) button (bit 3). */
BUTTON_LEFT_TOP = 8,
/** ``8``, Top left (power) button (bit 3). */
BUTTON_RESET = 8,
};
#else
#include "epicardium.h"
#endif
typedef enum {
ITEM_DRAW_BACKGROUND, /* draws the round rectangle highlighting selected item,
*/
ITEM_DRAW,
ITEM_HEIGHT, /* returns vertical space occupied by item */
ITEM_ACTIVATE, /* upper right from menu, can for a toggle button
or button activating trigger event
*/
ITEM_ACTIVE_EVENTS, /* handle events for when this item is selected and
activated */
ITEM_DRAW_FULLSCREEN /* draw this item full screen, makes most sense
for menu and other custom top-level handlers
*/
} UiItemAction;
enum _UiEventType
{
UI_PRESSED,
UI_PRESS,
UI_PRESS_REPEAT,
UI_LONG_PRESS,
UI_RELEASE,
};
typedef enum _UiEventType UiEventType;
int ui_event_update (Ctx *ctx);
int ui_event_match (int button, UiEventType type);
/* XXX globals */
extern int frame_no;
extern unsigned int ui_scene_timer;
extern int ui_no; /* the item in the UI to run fullscreen,
if it is a menu, that menu is rendered -
but this can also be used for other
UI control, where the menus are only
one of the states
*/
extern float ui_font_size;
extern float ui_line_height;
extern float ui_y;
extern float ui_x;
//extern int ui_start_menu;
typedef struct _UiItem UiItem;
struct _UiItem{
const char *title;
float (*handler)(Ctx *ctx, UiItem *item, UiItemAction action);
void *value; /* */
void *data;
float arg1;
float arg2;
float arg3;
};
void ui_set_items (UiItem *items);
float ui_item_draw_fullscreen (Ctx *ctx, UiItem *item);
float ui_item_height (Ctx *ctx, UiItem *item);
float ui_menu (Ctx *ctx, UiItem *item, UiItemAction action);
float ui_label (Ctx *ctx, UiItem *item, UiItemAction action);
float ui_separator (Ctx *ctx, UiItem *item, UiItemAction action);
float ui_button (Ctx *ctx, UiItem *item, UiItemAction action);
float ui_boolean_toggle (Ctx *ctx, UiItem *item, UiItemAction action);
float ui_float_slider (Ctx *ctx, UiItem *item, UiItemAction action);
float ui_int_slider (Ctx *ctx, UiItem *item, UiItemAction action);
float ui_sub_menu (Ctx *ctx, UiItem *item, UiItemAction action);
float ui_scene (Ctx *ctx, UiItem *item, UiItemAction action);
//unsigned int ui_ticks_live (void);
extern unsigned int ui_ticks;
extern int ui_autoplay;
extern int ui_ui_autoplay_timeout;
extern int ui_title_timeout;
extern float ui_scroll_jump;
UiItem *ui_current_scene (void);
void ui_reset_timer (void);
//int ui_find_menu (const char *name);
void ui_advance (void);
void ui_reverse (void);
int ui_event_update ();
int ui_event_match (int button, UiEventType event_type);
#endif
subdir('lib/') subdir('lib/')
subdir('blinky/') subdir('blinky/')
subdir('ctx-demo/')
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment