Compare commits

...

2 commits

6 changed files with 278 additions and 91 deletions

View file

@ -3,16 +3,36 @@
#include <stddef.h>
#include "keyboard.h"
#include "texture.h"
struct Component {
enum {
CPNT_TYPE_TEXT_INPUT,
CPNT_TYPE_BUTTON,
CPNT_TYPE_LABEL,
} type;
union {
struct TextInput { char text[64]; } text_input;
struct Button { const char *text; void (*on_click)(void*, struct Component *); } button;
struct Label { char text[64]; } label;
} data;
struct Component *next;
// this is filled automatically; DO NOT FILL THIS MANUALLY!
struct rect rect;
};
struct AppCreation {
void *ptr;
struct Component *root;
};
struct app_template {
void *(*create)(size_t width, size_t height, void *arg);
struct AppCreation (*create)(size_t width, size_t height, void *arg);
void (*init)(void *self);
void (*destroy)(void *self);
void (*event_keyboard)(void *self, struct key_event event);
};
// apps don't interact with framebuffer directly, instead they use these drawing functions.
void app_draw_pixel(void *self, struct color col, int x, int y);
void app_draw_rect(void *self, struct color col, const struct rect *rect);

314
src/app.c
View file

@ -32,12 +32,15 @@ struct app_instance {
struct tex framebuf;
// each instance has its own space for font rendering
struct tex fontbuf;
struct Component *component_root;
struct Component *component_focused;
struct app_instance *next_app;
struct app_instance *prev_app;
};
struct slab_cache app_slab_cache;
struct slab_cache fontbuf_slab_cache;
struct slab_cache component_cache;
struct app_instance *app_root = NULL;
@ -56,6 +59,7 @@ void app_init_global(void) {
slab_cache_init(&app_slab_cache, sizeof(struct app_instance), NULL, NULL);
slab_cache_init(&fontbuf_slab_cache, font_width(&app_font) * font_height(&app_font) * 4, NULL, NULL);
slab_cache_init(&component_cache, sizeof(struct Component), NULL, NULL);
extern struct app_template app_launcher_template;
ASSERT(app_push(&app_launcher_template, NULL, NULL));
@ -63,6 +67,137 @@ void app_init_global(void) {
atomic_store(&app_initialized, true);
}
void layout(struct Component *cpnt) {
int x = 0, y = 0;
unsigned fw = app_text_width(), fh = app_text_height();
// at the moment everything has the same size
for ( ; cpnt; cpnt = cpnt->next) {
cpnt->rect = RECT_XYWH(x, y, 20 * fw + 6, fh + 6);
y += fh + 6;
}
}
struct MyApp {
struct Component *one;
struct Component *two;
struct Component *out;
} my_app_instance; // TODO dynamic
static struct app_instance *app_find(void *app);
void draw_focus(void *app, struct Component *cpnt) {
app_draw_rect(app, COLOR_RGB(255, 0, 0), &RECT_XYWH(cpnt->rect.x, cpnt->rect.y, RECT_WIDTH(cpnt->rect), 1));
app_draw_rect(app, COLOR_RGB(255, 0, 0), &RECT_XYWH(cpnt->rect.x, cpnt->rect.y + RECT_HEIGHT(cpnt->rect) - 1, RECT_WIDTH(cpnt->rect), 1));
app_draw_rect(app, COLOR_RGB(255, 0, 0), &RECT_XYWH(cpnt->rect.x, cpnt->rect.y, 1, RECT_HEIGHT(cpnt->rect)));
app_draw_rect(app, COLOR_RGB(255, 0, 0), &RECT_XYWH(cpnt->rect.x + RECT_WIDTH(cpnt->rect) - 1, cpnt->rect.y, 1, RECT_HEIGHT(cpnt->rect)));
}
void redraw(void *app, struct Component *cpnt) {
switch (cpnt->type) {
case CPNT_TYPE_TEXT_INPUT:
app_draw_rect(app, COLOR_LUMA(255), &cpnt->rect);
app_draw_rect(app, COLOR_LUMA(0), &RECT_XYWH(
cpnt->rect.x + 1,
cpnt->rect.y + 1,
RECT_WIDTH(cpnt->rect) - 2,
RECT_HEIGHT(cpnt->rect) - 2
));
app_draw_rect(app, COLOR_LUMA(255), &RECT_XYWH(
cpnt->rect.x + 2,
cpnt->rect.y + 2,
RECT_WIDTH(cpnt->rect) - 4,
RECT_HEIGHT(cpnt->rect) - 4
));
app_draw_text(app, cpnt->data.text_input.text, cpnt->rect.x + 3, cpnt->rect.y + 3, COLOR_LUMA(0), COLOR_LUMA(255));
break;
case CPNT_TYPE_BUTTON:
app_draw_rect(app, COLOR_LUMA(150), &cpnt->rect);
app_draw_rect(app, COLOR_LUMA(0), &RECT_XYWH(
cpnt->rect.x + 1,
cpnt->rect.y + 1,
RECT_WIDTH(cpnt->rect) - 2,
RECT_HEIGHT(cpnt->rect) - 2
));
app_draw_rect(app, COLOR_LUMA(150), &RECT_XYWH(
cpnt->rect.x + 2,
cpnt->rect.y + 2,
RECT_WIDTH(cpnt->rect) - 4,
RECT_HEIGHT(cpnt->rect) - 4
));
app_draw_text(app, cpnt->data.button.text, cpnt->rect.x + 3, cpnt->rect.y + 3, COLOR_LUMA(0), COLOR_LUMA(150));
break;
case CPNT_TYPE_LABEL:
app_draw_rect(app, COLOR_LUMA(255), &cpnt->rect);
app_draw_rect(app, COLOR_LUMA(0), &RECT_XYWH(
cpnt->rect.x + 1,
cpnt->rect.y + 1,
RECT_WIDTH(cpnt->rect) - 2,
RECT_HEIGHT(cpnt->rect) - 2
));
app_draw_rect(app, COLOR_LUMA(255), &RECT_XYWH(
cpnt->rect.x + 2,
cpnt->rect.y + 2,
RECT_WIDTH(cpnt->rect) - 4,
RECT_HEIGHT(cpnt->rect) - 4
));
app_draw_text(app, cpnt->data.label.text, cpnt->rect.x + 3, cpnt->rect.y + 3, COLOR_LUMA(0), COLOR_LUMA(255));
break;
}
}
void on_click(void *self, struct Component *btn) {
struct MyApp *app = self;
uint64_t one_val;
uint64_t two_val;
const char *s;
s = app->one->data.text_input.text;
ASSERT(parse_u64(&s, &one_val));
s = app->two->data.text_input.text;
ASSERT(parse_u64(&s, &two_val));
char *out = app->out->data.label.text;
uint64_t result = one_val + two_val;
do {
*out++ = '0' + (result % 10);
result /= 10;
} while (result > 0);
*out = 0;
unsigned len = out - app->out->data.label.text;
out = app->out->data.label.text;
for (int i = 0; i < len/2; i++) {
char tmp = out[i];
out[i] = out[len - i - 1];
out[len - i - 1] = tmp;
}
redraw(self, app->out);
}
struct AppCreation my_app_create(size_t width, size_t height, void *arg) {
struct Component *one = slab_cache_alloc(&component_cache);
one->type = CPNT_TYPE_TEXT_INPUT;
one->data.text_input.text[0] = 0;
struct Component *two = slab_cache_alloc(&component_cache);
two->type = CPNT_TYPE_TEXT_INPUT;
two->data.text_input.text[0] = 0;
struct Component *btn = slab_cache_alloc(&component_cache);
btn->type = CPNT_TYPE_BUTTON;
btn->data.button.text = "calculate";
btn->data.button.on_click = on_click;
struct Component *out = slab_cache_alloc(&component_cache);
out->type = CPNT_TYPE_LABEL;
out->data.label.text[0] = 0;
one->next = two;
two->next = btn;
btn->next = out;
out->next = NULL;
my_app_instance.one = one;
my_app_instance.two = two;
my_app_instance.out = out;
return (struct AppCreation) {
.ptr = &my_app_instance,
.root = one,
};
}
static struct app_instance *app_find(void *app) {
if (app_root->app == app) {
@ -120,7 +255,6 @@ struct rect app_rect_local_to_global(struct app_instance *ins, const struct rect
return real_rect;
}
// TODO adapt to tex_blit interface (src and dst rect)
void app_draw_rect(void *self, struct color col, const struct rect *rect) {
struct app_instance *ins = app_find(self);
ASSERT(ins != NULL);
@ -193,8 +327,8 @@ bool app_push(struct app_template *template, void *arg, struct rect *pos) {
} else {
actual_pos = fb_rect();
}
void *app = template->create(RECT_WIDTH(actual_pos), RECT_HEIGHT(actual_pos), arg);
if (app == NULL) {
struct AppCreation creation = template->create(RECT_WIDTH(actual_pos), RECT_HEIGHT(actual_pos), arg);
if (creation.ptr == NULL) {
return false;
}
// allocate framebuf
@ -208,13 +342,21 @@ bool app_push(struct app_template *template, void *arg, struct rect *pos) {
ASSERT(ram_alloc_buffer(&ppn, req));
void *fontbuf = slab_cache_alloc(&fontbuf_slab_cache);
ASSERT(fontbuf != NULL);
// layout
layout(creation.root);
if (app_root != NULL && app_root->component_focused) {
redraw(app_root->app, app_root->component_focused);
}
// TODO this structure should be locked
struct app_instance *new_app = slab_cache_alloc(&app_slab_cache);
ASSERT(new_app != NULL);
*new_app = (struct app_instance){
.template = template,
.pos = actual_pos,
.app = app,
.app = creation.ptr,
.framebuf = TEX_CREATE(
RECT_WIDTH(actual_pos),
RECT_HEIGHT(actual_pos),
@ -225,6 +367,8 @@ bool app_push(struct app_template *template, void *arg, struct rect *pos) {
font_height(&app_font),
fontbuf
),
.component_root = creation.root,
.component_focused = NULL,
.next_app = app_root != NULL ? app_root : new_app,
.prev_app = app_root != NULL ? app_root->prev_app : new_app
};
@ -234,7 +378,12 @@ bool app_push(struct app_template *template, void *arg, struct rect *pos) {
}
app_root = new_app;
template->init(app);
for (struct Component *c = creation.root; c; c = c->next) {
redraw(creation.ptr, c);
}
if (template->init)
template->init(creation.ptr);
fb_refresh();
return true;
}
@ -277,10 +426,10 @@ void app_translate(struct app_instance *ins, int dx, int dy) {
extern struct app_template app_launcher_template;
void app_key_event_listener(struct key_event event) {
if (app_root == NULL) {
struct app_instance *ins = app_top();
if (ins == NULL) {
return;
}
struct app_instance *ins = app_top();
if (event.code == KEY_ARROW_LEFT && (event.mod & KEY_MOD_SHIFT)) {
if (event.pressed && ins->template != &app_launcher_template) {
@ -309,6 +458,11 @@ void app_key_event_listener(struct key_event event) {
if (event.code == KEY_TAB && (event.mod & KEY_MOD_SHIFT)) {
// select next app to be the current one now (natural rotation, easy for now)
if (event.pressed) {
// remove focus from the thing it's currently on
if (ins->component_focused) {
redraw(ins->app, ins->component_focused);
ins->component_focused = NULL;
}
app_root = app_root->next_app;
// redraw this new top window
struct tex fb_tex = fb_as_tex();
@ -328,89 +482,129 @@ void app_key_event_listener(struct key_event event) {
return;
}
if (ins->template->event_keyboard) {
ins->template->event_keyboard(ins->app, event);
if (event.code == KEY_TAB && event.pressed) {
struct Component *old_component = ins->component_focused;
if (ins->component_focused == NULL) {
ins->component_focused = ins->component_root;
} else {
if (ins->component_focused->next) {
ins->component_focused = ins->component_focused->next;
} else {
ins->component_focused = ins->component_root;
}
}
if (old_component)
redraw(ins->app, old_component);
draw_focus(ins->app, ins->component_focused);
}
// TODO text field input based on focused element
if (ins->component_focused) {
if (ins->component_focused->type == CPNT_TYPE_BUTTON) {
if (event.code == KEY_ENTER && event.pressed) {
ins->component_focused->data.button.on_click(ins->app, ins->component_focused);
}
} else if (ins->component_focused->type == CPNT_TYPE_TEXT_INPUT) {
if (event.code == KEY_BACKSPACE && event.pressed) {
uint8_t *start = (uint8_t*)ins->component_focused->data.text_input.text;
uint8_t *c = start;
while (*c != 0) {
c++;
}
if (c > start) {
*--c = 0;
redraw(ins->app, ins->component_focused);
}
}
if (event.printable && event.pressed) {
uint8_t *c = (uint8_t*)ins->component_focused->data.text_input.text;
while (*c != 0) {
c++;
}
utf8_write(c, event.printable);
redraw(ins->app, ins->component_focused);
}
}
}
}
extern struct app_template app_kbdbg_template;
extern struct app_template snake_template;
extern struct app_template tron_template;
extern struct app_template app_kernmsg_template;
struct app_template my_app_template = {
.create = my_app_create,
.init = NULL,
.destroy = NULL
};
#define KNOWN_APPS_SIZE 10
struct {
const char *name;
struct app_template *template;
} known_apps[KNOWN_APPS_SIZE] = {
} known_apps[] = {
{
"keyboard-debug",
&app_kbdbg_template,
"aaa",
&my_app_template,
},
{
"snake",
&snake_template,
"bbbb",
&my_app_template,
},
{
"tron",
&tron_template,
},
{
"kernmsg",
&app_kernmsg_template,
}
};
size_t num_known_apps = 4;
#define NUM_KNOWN_APPS (sizeof(known_apps)/sizeof(known_apps[0]))
#define FONT_TEX_NUM_PIXELS 10*20
struct app_launcher {
int current_app;
struct color fg;
struct color bg;
struct Component *label;
};
void *app_launcher_create(size_t width, size_t height, void *arg) {
(void) arg;
struct app_launcher *app = ram_alloc_page_zeroed_asserted();
return app;
void button_next(void *self, struct Component *cpnt) {
struct app_launcher *app = self;
app->current_app = (app->current_app + 1) % NUM_KNOWN_APPS;
// TODO to we have strcpy?
const char *c = known_apps[app->current_app].name;
char *d = app->label->data.label.text;
while (*c) { *d++ = *c++; }
redraw(self, app->label);
}
void app_launcher_init(void *self) {
void button_launch(void *self, struct Component *cpnt) {
struct app_launcher *app = self;
app_push(known_apps[app->current_app].template, NULL, &RECT_XYWH(100, 100, 400, 400));
}
struct AppCreation app_launcher_create(size_t width, size_t height, void *arg) {
(void) arg;
struct app_launcher *app = ram_alloc_page_zeroed_asserted();
app->current_app = 0;
app->fg = COLOR_RGB(0, 255, 100);
app->bg = COLOR_LUMA(50);
app_draw_rect(self, app->bg, NULL);
app_draw_text(self, known_apps[app->current_app].name, 0, 0, app->fg, app->bg);
struct Component *label = slab_cache_alloc(&component_cache);
app->label = label;
label->type = CPNT_TYPE_LABEL;
// TODO to we have strcpy?
const char *c = known_apps[app->current_app].name;
char *d = label->data.label.text;
while (*c) { *d++ = *c++; }
struct Component *btn_next = slab_cache_alloc(&component_cache);
btn_next->type = CPNT_TYPE_BUTTON;
btn_next->data.button.text = "next";
btn_next->data.button.on_click = button_next;
struct Component *btn_launch = slab_cache_alloc(&component_cache);
btn_launch->type = CPNT_TYPE_BUTTON;
btn_launch->data.button.text = "launch";
btn_launch->data.button.on_click = button_launch;
label->next = btn_next;
btn_next->next = btn_launch;
btn_launch->next = NULL;
return (struct AppCreation) { app, label };
}
void app_launcher_destroy(void *self) {
// TODO free other stuff
ram_free(ppn_from_aligned_pa(pa_from_pointer(self)));
}
void app_launcher_event_keyboard(void *self, struct key_event ev) {
struct app_launcher *app = self;
bool changed = false;
if (ev.code == KEY_ARROW_RIGHT && ev.pressed) {
app->current_app = (app->current_app + 1) % num_known_apps;
changed = true;
} else if (ev.code == KEY_ARROW_LEFT && ev.pressed) {
app->current_app = (app->current_app - 1) % num_known_apps;
changed = true;
}
if (changed) {
app_draw_rect(app, app->bg, NULL);
app_draw_text(self, known_apps[app->current_app].name, 0, 0, app->fg, app->bg);
}
if (ev.code == KEY_ENTER && ev.pressed) {
app_push(known_apps[app->current_app].template, NULL, &RECT_XYWH(100, 100, 400, 400));
}
}
struct app_template app_launcher_template = {
.create = app_launcher_create,
.init = app_launcher_init,
.init = NULL,
.destroy = app_launcher_destroy,
.event_keyboard = app_launcher_event_keyboard,
};

View file

@ -72,10 +72,3 @@ void app_kernmsg_event_keyboard(void *self, struct key_event ev) {
app_kernmsg_redraw(app);
}
}
struct app_template app_kernmsg_template = {
.create = app_kernmsg_create,
.destroy = app_kernmsg_destroy,
.init = app_kernmsg_init,
.event_keyboard = app_kernmsg_event_keyboard,
};

View file

@ -3,6 +3,7 @@
#include "ram.h"
#include "std.h"
#include "unicode.h"
#include "keyboard.h"
#define FONT_TEX_NUM_PIXELS 10*20
@ -96,10 +97,3 @@ void app_kbdbg_event_keyboard(void *self, struct key_event ev) {
app->pix_pos_x = 0;
app->pix_pos_y += fh;
}
struct app_template app_kbdbg_template = {
.create = app_kbdbg_create,
.init = app_kbdbg_init,
.destroy = app_kbdbg_destroy,
.event_keyboard = app_kbdbg_event_keyboard,
};

View file

@ -445,10 +445,3 @@ void tron_event_keyboard(void *self, struct key_event ev) {
default: UNREACHABLE();
}
}
struct app_template tron_template = {
.create = tron_create,
.init = tron_init,
.destroy = tron_destroy,
.event_keyboard = tron_event_keyboard,
};

View file

@ -232,10 +232,3 @@ void snake_destroy(void *self) {
timer_cancel(&game->timer);
ram_free(ppn_from_aligned_pa(pa_from_pointer(self)));
}
struct app_template snake_template = (struct app_template) {
.create = snake_create,
.init = snake_init,
.destroy = snake_destroy,
.event_keyboard = snake_key_event_listener,
};