From 2a6a9318947d90aab1ff53c801900c15f80ed6c5 Mon Sep 17 00:00:00 2001 From: Mid <> Date: Sun, 6 Jul 2025 21:19:45 +0300 Subject: [PATCH] Add k3Menu source --- luma/wm/immdraw.h | 73 +++++++++ luma/wm/k3menu.c | 373 ++++++++++++++++++++++++++++++++++++++++++++++ luma/wm/k3menu.h | 207 +++++++++++++++++++++++++ 3 files changed, 653 insertions(+) create mode 100644 luma/wm/immdraw.h create mode 100644 luma/wm/k3menu.c create mode 100644 luma/wm/k3menu.h diff --git a/luma/wm/immdraw.h b/luma/wm/immdraw.h new file mode 100644 index 0000000..097b5d8 --- /dev/null +++ b/luma/wm/immdraw.h @@ -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; +} diff --git a/luma/wm/k3menu.c b/luma/wm/k3menu.c new file mode 100644 index 0000000..9101fbe --- /dev/null +++ b/luma/wm/k3menu.c @@ -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; +} diff --git a/luma/wm/k3menu.h b/luma/wm/k3menu.h new file mode 100644 index 0000000..eada4ec --- /dev/null +++ b/luma/wm/k3menu.h @@ -0,0 +1,207 @@ +#pragma once + +#include +#include +#include +#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; +} +