Add k3Menu source

This commit is contained in:
Mid 2025-07-06 21:19:45 +03:00
parent 93b885cb43
commit 2a6a931894
3 changed files with 653 additions and 0 deletions

73
luma/wm/immdraw.h Normal file
View File

@ -0,0 +1,73 @@
#pragma once
#include"../vid/api.h"
#include"../sys.h"
extern void *VidLink;
static inline void immdraw_font_draw(uint16_t x, uint16_t y, uint16_t w, size_t txtlen, const char *txt, uint8_t align) {
LumaVidCommand *cmd = VidLink;
cmd->op = LUMA_VID_COMMAND_SET_COLOR_FLAT;
cmd->colorFlat.r = 0x00;
cmd->colorFlat.g = 0x00;
cmd->colorFlat.b = 0x00;
cmd = (LumaVidCommand*) ((uintptr_t) cmd + sizeof(cmd->colorFlat));
cmd->op = LUMA_VID_COMMAND_TEXT;
cmd->text.x = x;
cmd->text.y = y;
cmd->text.wall = x + w;
cmd->text.align = align;
cmd->text.len = txtlen;
std_copy(cmd->text.data, txt, txtlen);
cmd = (LumaVidCommand*) ((uintptr_t) cmd + sizeof(cmd->text) + txtlen);
cmd->op = LUMA_VID_COMMAND_END;
memory_barrier();
sys_signal_send(VidLink, -1);
sys_signal_wait(VidLink, NULL);
}
static inline void immdraw_fill_rect(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint8_t r, uint8_t g, uint8_t b) {
LumaVidCommand *cmd = VidLink;
cmd->op = LUMA_VID_COMMAND_SET_COLOR_FLAT;
cmd->colorFlat.r = r;
cmd->colorFlat.g = g;
cmd->colorFlat.b = b;
cmd = (LumaVidCommand*) ((uintptr_t) cmd + sizeof(cmd->colorFlat));
cmd->op = LUMA_VID_COMMAND_RECT;
cmd->rect.x1 = x;
cmd->rect.x2 = x + w - 1;
cmd->rect.y1 = y;
cmd->rect.y2 = y + h - 1;
cmd = (LumaVidCommand*) ((uintptr_t) cmd + sizeof(cmd->rect));
cmd->op = LUMA_VID_COMMAND_END;
memory_barrier();
sys_signal_send(VidLink, -1);
sys_signal_wait(VidLink, NULL);
}
static inline void immdraw_text_extent(size_t txtlen, const uint8_t *txt, size_t wall, uint16_t *w, uint16_t *h) {
LumaVidCommand *cmd = VidLink;
cmd->op = LUMA_VID_COMMAND_TEXT_EXTENT;
cmd->textExtent.wall = wall;
cmd->textExtent.len = txtlen;
std_copy(cmd->textExtent.data, txt, txtlen);
cmd = (LumaVidCommand*) ((uintptr_t) cmd + sizeof(cmd->textExtent) + txtlen);
cmd->op = LUMA_VID_COMMAND_END;
memory_barrier();
sys_signal_send(VidLink, -1);
sys_signal_wait(VidLink, NULL);
*w = cmd->textExtent.w;
*h = cmd->textExtent.h;
}

373
luma/wm/k3menu.c Normal file
View File

@ -0,0 +1,373 @@
#include"k3menu.h"
#include"immdraw.h"
#include"../ps2/api.h"
#include"tiny.h"
#define memset __builtin_memset
#define memcpy __builtin_memcpy
void k3MenuSetBounds_(struct k3MObj *this, int16_t x, int16_t y, int16_t w, int16_t h) {
this->x = x;
this->y = y;
this->w = w;
this->h = h;
}
static bool event_send_nonrecur(struct k3MEvent *ev) {
bool handled = false;
for(size_t i = 0; i < ev->target->handlerCount; i++) {
struct k3MEventHandler *h = &ev->target->handlers[i];
if(h->code == ev->code || h->code == k3M_EVENT_ALL) {
if(h->callback(ev, h->ud)) {
return true;
}
}
}
return handled;
}
int k3MRegisterEventHandler_(struct k3MObj *obj, uint16_t evcode, k3MEventHandlerFunc callback, uint8_t *ud, size_t udSize) {
if(ud && udSize > k3M_USERDATA_SIZE) {
return 0;
}
obj->handlers = realloc(obj->handlers, sizeof(*obj->handlers) * ++obj->handlerCount);
obj->handlers[obj->handlerCount - 1].code = evcode;
obj->handlers[obj->handlerCount - 1].callback = callback;
if(ud) {
memcpy(obj->handlers[obj->handlerCount - 1].ud, ud, udSize);
}
return 1;
}
bool k3MEventSend(struct k3MEvent *ev) {
while(!event_send_nonrecur(ev)) {
if(ev->kind == k3M_EVENTKIND_DIRECT) {
return false;
} else if(ev->kind == k3M_EVENTKIND_BUBBLE) {
ev->target = ev->target->parent;
if(ev->target == ev->original || !ev->target) {
return false;
}
} else if(ev->kind == k3M_EVENTKIND_CAPTURE) {
struct k3MObj *parent = ev->target;
for(size_t i = 0; i < parent->childCount; i++) {
if(!parent->children[i]->invisible) {
ev->target = parent->children[i];
if(k3MEventSend(ev)) {
return true;
}
}
}
return false;
}
}
return true;
}
static void obj_draw(struct k3MObj *this) {
struct k3MProperty *borderRadiusProp = k3MFindProperty(this, k3M_PROP_BORDER_RADIUS, false);
//float borderRadius = borderRadiusProp ? borderRadiusProp->si[0] : 0;
struct k3MProperty *bgColorProp = k3MFindProperty(this, k3M_PROP_BG_COLOR, false);
uint8_t r = 0, g = 0, b = 0, a = 0;
if(bgColorProp) {
r = bgColorProp->si[0];
g = bgColorProp->si[1];
b = bgColorProp->si[2];
a = bgColorProp->si[3];
}
if(a != 0) {
immdraw_fill_rect(this->x, this->y, this->w, this->h, r, g, b);
}
}
static bool label_draw(struct k3MEvent *ev, uint8_t *ud) {
struct k3MLabel *this = (void*) ev->target;
obj_draw((void*) this);
if(this->txt) {
immdraw_font_draw(this->x, this->y, this->w, strlen(this->txt), this->txt, LUMA_VID_TEXT_ALIGN_CENTER);
}
return false;
}
struct k3MLabel *k3MLabel(struct k3Font *font, uint32_t sz, const char *txt) {
struct k3MLabel *ret = calloc(1, sizeof(*ret));
ret->font = font;
ret->sz = sz;
ret->txt = txt;
k3MRegisterEventHandler(ret, k3M_EVENT_DRAW, label_draw, NULL, 0);
return ret;
}
int k3MRemoveChild(struct k3MObj *parent, struct k3MObj *child) {
for(size_t i = 0; i < parent->childCount; i++) {
if(parent->children[i] == child) {
memcpy(&parent->children[i], &parent->children[i + 1], sizeof(*parent->children) * (parent->childCount - i - 1));
parent->childCount--;
child->parent = NULL;
return 1;
}
}
return 0;
}
void k3MAddChild(struct k3MObj *parent, struct k3MObj *child) {
if(child->parent) {
k3MRemoveChild(child->parent, child);
}
parent->children = realloc(parent->children, sizeof(*parent->children) * (++parent->childCount));
parent->children[parent->childCount - 1] = child;
child->parent = parent;
if(parent->layout) {
parent->layout(parent);
}
}
void k3MOverrideProperty(struct k3MObj *obj, struct k3MProperty n) {
struct k3MProperty *o = k3MFindProperty(obj, n.type, true);
if(!o) {
obj->properties = realloc(obj->properties, ++obj->propertyCount * sizeof(*obj->properties));
o = &obj->properties[obj->propertyCount - 1];
}
memcpy(o, &n, sizeof(n));
}
struct k3MProperty *k3MFindProperty(struct k3MObj *obj, enum k3MPropertyType t, bool direct) {
for(size_t i = 0; i < obj->propertyCount; i++) {
if(obj->properties[i].type == t) {
return &obj->properties[i];
}
}
if(direct || !obj->parent) {
return NULL;
}
return k3MFindProperty(obj->parent, t, direct);
}
static bool screen_ev(struct k3MEvent *ev, uint8_t *ud) {
struct k3MScreen *this = (void*) ev->target;
if(ev->code == k3M_EVENT_MOUSE_PRESS || ev->code == k3M_EVENT_MOUSE_RELEASE || ev->code == k3M_EVENT_MOUSE_MOTION) {
struct k3MObj *innermost = (void*) this;
while(innermost->childCount != 0) {
bool foundInner = false;
for(intmax_t i = innermost->childCount - 1; i >= 0; i--) {
struct k3MObj *child = innermost->children[i];
if(!child->invisible && ev->mouse.x >= child->x && ev->mouse.x < child->x + child->w && ev->mouse.y >= child->y && ev->mouse.y < child->y + child->h) {
innermost = child;
foundInner = true;
break;
}
}
if(!foundInner) {
break;
}
}
if(innermost != this->lastHover) {
this->lastHover->hovered = false;
innermost->hovered = true;
this->lastHover = innermost;
}
if(innermost != (void*) this) {
if(ev->code == k3M_EVENT_MOUSE_PRESS) {
this->keyboardFocus = innermost;
}
ev->kind = k3M_EVENTKIND_BUBBLE;
ev->target = innermost;
k3MEventSend(ev);
if(ev->code == k3M_EVENT_MOUSE_RELEASE) {
ev->code = k3M_EVENT_MOUSE_CLICK;
ev->kind = k3M_EVENTKIND_DIRECT;
ev->target = innermost;
k3MEventSend(ev);
}
}
return true;
} else if(ev->code == k3M_EVENT_KEY_PRESS || ev->code == k3M_EVENT_KEY_RELEASE || ev->code == k3M_EVENT_INPUT) {
if(this->keyboardFocus) {
ev->kind = k3M_EVENTKIND_BUBBLE;
ev->target = (void*) this->keyboardFocus;
asm volatile("xchg %%bx, %%bx":::);
k3MEventSend(ev);
}
return true;
}
ev->kind = k3M_EVENTKIND_CAPTURE;
for(intmax_t i = this->childCount; i --> 0;) {
if(!this->children[i]->invisible) {
ev->target = this->children[i];
if(k3MEventSend(ev)) {
return true;
}
}
}
return false;
}
struct k3MScreen *k3MScreen() {
struct k3MScreen *ret = calloc(1, sizeof(*ret));
ret->lastHover = (void*) ret;
k3MRegisterEventHandler(ret, k3M_EVENT_ALL, screen_ev, NULL, 0);
return ret;
}
static bool textbutton_draw(struct k3MEvent *ev, uint8_t *ud) {
struct k3MTextButton *this = (void*) ev->target;
obj_draw((void*) this);
if(this->txt) {
//struct k3RectF txtsz;
//k3FontSz(this->font, this->sz, this->txt, &txtsz);
//k3FontDraw(this->font, this->x + this->w / 2 - txtsz.w / 2, this->y, this->sz, this->txt, (vec4) {
// 1 - 0.8 * this->disabled, 1 - 0.8 * this->disabled, 1 - 0.8 * this->disabled, 1});
int top = 1, right = 1, bottom = 1, left = 1;
struct k3MProperty *marginProp = k3MFindProperty(this, k3M_PROP_MARGIN, false);
if(marginProp) {
top = marginProp->si[0];
right = marginProp->si[1];
bottom = marginProp->si[2];
left = marginProp->si[3];
}
immdraw_font_draw(this->x + top, this->y + left, this->w - left - right, strlen(this->txt), this->txt, LUMA_VID_TEXT_ALIGN_CENTER);
}
return false;
}
struct k3MTextButton *k3MTextButton(struct k3Font *font, uint32_t sz, const char *txt) {
struct k3MTextButton *ret = calloc(1, sizeof(*ret));
ret->font = font;
ret->sz = sz;
ret->txt = txt;
k3MRegisterEventHandler(ret, k3M_EVENT_DRAW, textbutton_draw, NULL, 0);
return ret;
}
static bool textinput_draw(struct k3MEvent *ev, uint8_t *ud) {
struct k3MTextInput *this = (void*) ev->target;
obj_draw((void*) this);
if(this->txt) {
int isPlaceholder = strlen(this->txt) == 0;
const char *txt = isPlaceholder ? this->placeholder : this->txt;
//struct k3RectF txtsz;
//immdraw_font_getsize(this->font, this->sz, txt, &txtsz);
immdraw_font_draw(this->x + 2, this->y + 2, this->w, strlen(txt), txt, LUMA_VID_TEXT_ALIGN_LEFT);
}
return false;
}
static bool textinput_key(struct k3MEvent *ev, uint8_t *ud) {
struct k3MTextInput *this = (void*) ev->target;
if(ev->key.num == LK_BACKSPACE && ev->code != k3M_EVENT_KEY_RELEASE) {
char *last = k3UTF8LastCodepointZ(this->txt);
if(last) {
*last = 0;
}
} else if(ev->key.num == LK_ENTER && ev->code == k3M_EVENT_KEY_PRESS) {
struct k3MEvent newev = {
.code = k3M_EVENT_COMPLETE,
.kind = k3M_EVENTKIND_DIRECT,
.original = (void*) this,
.target = (void*) this,
};
k3MEventSend(&newev);
}
return true;
}
static bool textinput_char(struct k3MEvent *ev, uint8_t *ud) {
struct k3MTextInput *this = (void*) ev->target;
size_t len = strlen(this->txt);
this->txt = realloc(this->txt, len + 5);
len += k3UTF8Encode(ev->input.code, this->txt + len);
this->txt[len] = 0;
return true;
}
struct k3MTextInput *k3MTextInput(struct k3Font *font, uint32_t sz, const char *placeholder, const char *txt) {
struct k3MTextInput *ret = calloc(1, sizeof(*ret));
ret->font = font;
ret->sz = sz;
ret->placeholder = placeholder ? placeholder : "";
ret->txt = strdup(txt ? txt : "");
k3MRegisterEventHandler(ret, k3M_EVENT_DRAW, textinput_draw, NULL, 0);
k3MRegisterEventHandler(ret, k3M_EVENT_KEY_PRESS, textinput_key, NULL, 0);
k3MRegisterEventHandler(ret, k3M_EVENT_KEY_RELEASE, textinput_key, NULL, 0);
k3MRegisterEventHandler(ret, k3M_EVENT_INPUT, textinput_char, NULL, 0);
return ret;
}
struct k3MObj *k3MObj() {
struct k3MObj *ret = calloc(1, sizeof(*ret));
return ret;
}

207
luma/wm/k3menu.h Normal file
View File

@ -0,0 +1,207 @@
#pragma once
#include<stdint.h>
#include<stdbool.h>
#include<stddef.h>
#include"tiny.h"
struct k3Font;
struct k3MObj;
#define k3M_EVENT_MOUSE_ENTER 0
#define k3M_EVENT_MOUSE_MOTION 1
#define k3M_EVENT_MOUSE_LEAVE 2
#define k3M_EVENT_MOUSE_PRESS 3
#define k3M_EVENT_MOUSE_RELEASE 4
#define k3M_EVENT_MOUSE_CLICK 5
#define k3M_EVENT_KEY_PRESS 6
#define k3M_EVENT_KEY_RELEASE 7
#define k3M_EVENT_INPUT 8
#define k3M_EVENT_DRAW 9
#define k3M_EVENT_COMPLETE 10
#define k3M_EVENT_ALL 11
#define k3M_MOUSE_BUTTON_0 0
#define k3M_MOUSE_BUTTON_1 1
#define k3M_MOUSE_BUTTON_2 2
#define k3M_EVENTKIND_DIRECT 0
#define k3M_EVENTKIND_CAPTURE 1
#define k3M_EVENTKIND_BUBBLE 2
#define k3M_USERDATA_SIZE 16
#include"../std.h"
struct k3MEvent {
uint16_t code;
uint8_t kind;
struct k3MObj *original;
struct k3MObj *target;
union {
struct {
int button;
int16_t x;
int16_t y;
} mouse;
struct {
uint32_t num;
} key;
struct {
uint32_t code;
} input;
};
};
typedef bool(*k3MEventHandlerFunc)(struct k3MEvent*, uint8_t *ud);
typedef struct k3MEventHandler {
uint8_t ud[k3M_USERDATA_SIZE];
k3MEventHandlerFunc callback;
uint16_t code;
} k3MEventHandler;
enum k3MPropertyType {
k3M_PROP_BG_COLOR,
k3M_PROP_BORDER_RADIUS,
k3M_PROP_MARGIN,
};
struct k3MProperty {
enum k3MPropertyType type;
union {
intmax_t si[4];
void *ptr;
uint8_t buf[32];
};
};
typedef void(*k3MLayout)(struct k3MObj*);
struct k3MObj {
struct k3MObj *parent;
k3MLayout layout;
size_t childCount;
struct k3MObj **children;
int16_t x;
int16_t y;
int16_t w;
int16_t h;
bool invisible, hovered, disabled;
k3MEventHandler *handlers;
size_t handlerCount;
struct k3MProperty *properties;
size_t propertyCount;
};
struct k3MObj *k3MObj();
#define k3MenuSetBounds(a, x, y, w, h) k3MenuSetBounds_((struct k3MObj*) (a), (x), (y), (w), (h))
void k3MenuSetBounds_(struct k3MObj *this, int16_t x, int16_t y, int16_t w, int16_t h);
#define k3MRegisterEventHandler(a, c, cb, ud, udsz) k3MRegisterEventHandler_((struct k3MObj*) (a), (c), (cb), (ud), (udsz))
int k3MRegisterEventHandler_(struct k3MObj *obj, uint16_t evcode, k3MEventHandlerFunc callback, uint8_t *ud, size_t udSize);
bool k3MEventSend(struct k3MEvent *ev);
int k3MRemoveChild(struct k3MObj *parent, struct k3MObj *child);
void k3MAddChild(struct k3MObj *parent, struct k3MObj *child);
void k3MOverrideProperty(struct k3MObj *obj, struct k3MProperty);
struct k3MProperty *k3MFindProperty(struct k3MObj *obj, enum k3MPropertyType, bool direct);
struct k3MLabel {
struct k3MObj;
struct k3Font *font;
uint32_t sz;
char *txt;
};
struct k3MLabel *k3MLabel(struct k3Font *font, uint32_t millipts, const char *txt);
struct k3MScreen {
struct k3MObj;
struct k3MObj *keyboardFocus;
struct k3MObj *lastHover;
};
struct k3MScreen *k3MScreen();
struct k3MTextButton {
struct k3MObj;
struct k3Font *font;
uint32_t sz;
char *txt;
};
struct k3MTextButton *k3MTextButton(struct k3Font *font, uint32_t millipts, const char *txt);
struct k3MTextInput {
struct k3MObj;
struct k3Font *font;
uint32_t sz;
char *placeholder;
char *txt;
};
struct k3MTextInput *k3MTextInput(struct k3Font *font, uint32_t millipts, const char *placeholder, const char *txt);
static inline int k3UTF8Encode(uint32_t cp, uint8_t ret[static 4]) {
if(cp < 0x80) {
ret[0] = cp;
return 1;
} else if(cp < 0x800) {
ret[0] = 192 | (cp >> 6);
ret[1] = 128 | (cp & 63);
return 2;
} else if(cp < 0x10000) {
ret[0] = 224 | (cp >> 12);
ret[1] = 128 | ((cp >> 6) & 63);
ret[2] = 128 | (cp & 63);
return 3;
} else if(cp < 0x110000) {
ret[0] = 240 | (cp >> 18);
ret[1] = 128 | ((cp >> 12) & 63);
ret[2] = 128 | ((cp >> 6) & 63);
ret[3] = 128 | (cp & 63);
return 4;
}
return 0;
}
static inline size_t strlen(const char *str) {
size_t sz = 0;
while(*str) {
sz++;
str++;
}
return sz;
}
static inline const char *k3UTF8LastCodepointZ(const char *txt) {
size_t len = strlen(txt);
while(len--) {
if((txt[len] & 0xC0) != 0x80) {
return &txt[len];
}
}
return NULL;
}
static inline char *strdup(const char *s) {
size_t len = strlen(s);
char *d = tiny_malloc(len + 1);
d[len] = '\0';
__builtin_memcpy(d, s, len);
return d;
}