Saving progress. This is the moment I stopped understanding what I am doing

This commit is contained in:
Андреев Григорий 2026-01-08 01:46:14 +03:00
parent 8cb684d82e
commit 648621eb42
9 changed files with 199 additions and 159 deletions

View File

@ -116,4 +116,9 @@ NODISCARD VecU8 codegen_include_relative_to_root(SpanU8 bonus_ns, SpanU8 abs_pat
return res;
}
/* returns back `type` string, but if it is "", returns "void" instead */
SpanU8 c_type_empty_means_void(SpanU8 type){
return type.len > 0 ? type : cstr("void");
}
#endif

View File

@ -4,6 +4,7 @@
#include "l1_5_templ_very_base.h"
#include "margaret.h"
#include "lucy.h"
#include "gui.h"
int main() {
mkdir_nofail("l1_5");
@ -12,6 +13,7 @@ int main() {
generate_l1_5_template_instantiation_for_base_types();
generate_l1_5_template_instantiations_for_margaret();
generate_l1_5_lucy_headers();
generate_l1_5_gui_headers();
finish_layer(cstr("l1_5"));
return 0;
}

47
src/l1_5/anne/gui.h Normal file
View File

@ -0,0 +1,47 @@
#ifndef prototype1_src_l1_5_anne_gui_h
#define prototype1_src_l1_5_anne_gui_h
#include "../codegen/trait_wrap_boil.h"
void generate_l1_5_gui_headers(){
mkdir_nofail("l1_5/eve/gui");
SpanU8 l = cstr("l1_5"), ns = cstr("gui");
generate_trait_wrapper_templ_inst_eve_header(l, ns, (trait_wrapper_boil_options){
.trait = {
.name = cstr("Widget"),
.methods = (SpanNamedMethodSignatureRecordRef){
.data = (NamedMethodSignatureRecordRef[]){
{
.takes_self = true,
.takes_mut_self = true,
.params = (SpanNamedVariableRecordRef){
.data = (NamedVariableRecordRef[]){
{ .type = cstr("uvec2"), .name = cstr("max_limits") }
}, .len = 1
},
.return_type = cstr("uvec2"),
.name = cstr("DRAW_PREPARE"),
},
{
.takes_self = true,
.takes_mut_self = true,
.params = (SpanNamedVariableRecordRef){
.data = (NamedVariableRecordRef[]){
{ .type = cstr("ivec2"), .name = cstr("drawing_offset") },
{ .type = cstr("uvec2"), .name = cstr("surface_sz") },
{ .type = cstr("BorderS32"), .name = cstr("border") },
}, .len = 3
},
.return_type = cstr(""),
.name = cstr("DRAW"),
}
}, .len = 2
},
.drop_primitive = false,
.base_struct_name = cstr("Widget"),
},
.box = true, .mut_ref = true
});
}
#endif

View File

@ -24,7 +24,9 @@ void generate_l1_5_liza_headers() {
.name = cstr("ding"),
}
}, .len = 1
}
},
.drop_primitive = false,
.base_struct_name = cstr(""),
},
.box = true, .ref = true, .mut_ref = true
});

View File

@ -24,15 +24,19 @@ typedef struct {
SpanNamedMethodSignatureRecordRef methods;
bool drop_primitive;
SpanU8 name;
/* It is usually either name or "void" */
SpanU8 base_struct_name;
} NamedTraitDefRecordRef;
NODISCARD VecU8 generate_trait_table_structure(NamedTraitDefRecordRef trait){
VecU8 res = VecU8_from_cstr("typedef struct {\n");
for (size_t i = 0; i < trait.methods.len; i++) {
NamedMethodSignatureRecordRef method = *SpanNamedMethodSignatureRecordRef_at(trait.methods, i);
VecU8_append_vec(&res, VecU8_fmt(SPACE "%s (*%s)(", method.return_type, method.name));
VecU8_append_vec(&res, VecU8_fmt(SPACE "%s (*%s)(", c_type_empty_means_void(method.return_type), method.name));
if (method.takes_self) {
VecU8_append_span(&res, method.takes_mut_self ? cstr("void*") : cstr("const void*"));
VecU8_append_vec(&res, VecU8_fmt(
method.takes_mut_self ? "%s*" : "const %s*",
c_type_empty_means_void(trait.base_struct_name)));
}
for (size_t p = 0; p < method.params.len; p++) {
NamedVariableRecordRef param = *SpanNamedVariableRecordRef_at(method.params, p);
@ -43,7 +47,9 @@ NODISCARD VecU8 generate_trait_table_structure(NamedTraitDefRecordRef trait){
VecU8_append_span(&res, cstr(");\n"));
}
if (!trait.drop_primitive) {
VecU8_append_span(&res, cstr(SPACE "void (*drop)(void*);\n"));
VecU8_append_vec(&res, VecU8_fmt(
SPACE "void (*drop)(%s*);\n",
c_type_empty_means_void(trait.base_struct_name)));
}
VecU8_append_vec(&res, VecU8_fmt("} %s_Table;\n\n", trait.name));
return res;
@ -56,19 +62,33 @@ typedef struct {
bool mut_ref;
} trait_wrapper_boil_options;
/* (refkind, mut) in {(Ref, false), (MutRef, true), (Box, true)} */
void codegen_append_trait_wrapper_structure_some_refkind(VecU8* res, NamedTraitDefRecordRef trait,
SpanU8 refkind, bool mut){
VecU8_append_vec(res, VecU8_fmt(
"typedef struct {\n"
SPACE "%s%s* r;\n" /* epsilon / const, op.trait.base_struct_name */
SPACE "const %s_Table* t;\n" /* op.trait.name */
"} %s%s;\n\n", /* refkind, op.trait.name */
mut ? cstr("") : cstr("const "),
c_type_empty_means_void(trait.base_struct_name),
trait.name, refkind, trait.name));
}
/* (refkind, self_as_ptr) in {(Ref, false), (MutRef, false), (Box, true)} */
void codegen_append_trait_wrapper_some_method_some_refkind(VecU8* res, SpanU8 trait_name,
NamedMethodSignatureRecordRef method, SpanU8 refkind, bool self_as_ptr){
VecU8_append_vec(res, VecU8_fmt(
"%s %s%s_%s(%s%s%s self",
/* return_type, refkind, trait.name, method.name, refkind, trait.name */
method.return_type, refkind, trait_name, method.name, refkind, trait_name, self_as_ptr ? cstr("*") : cstr("")));
c_type_empty_means_void(method.return_type),
refkind, trait_name, method.name, refkind, trait_name, self_as_ptr ? cstr("*") : cstr("")));
for (size_t p = 0; p < method.params.len; p++) {
NamedVariableRecordRef param = method.params.data[p];
VecU8_append_vec(res, VecU8_fmt(", %s %s", param.type, param.name));
}
VecU8_append_span(res, cstr("){\n" SPACE));
if (!SpanU8_cont_equal(method.return_type, cstr("void")))
if (method.return_type.len > 0)
VecU8_append_span(res, cstr("return "));
VecU8_append_vec(res, VecU8_fmt("self%s""t->%s(",
self_as_ptr ? cstr("->") : cstr("."),
@ -95,12 +115,7 @@ NODISCARD VecU8 generate_trait_wrapper_boilerplate(trait_wrapper_boil_options op
VecU8 res = VecU8_new();
VecU8_append_vec(&res, generate_trait_table_structure(op.trait));
if (op.ref) {
VecU8_append_vec(&res, VecU8_fmt(
"typedef struct {\n"
SPACE "const void* r;\n"
SPACE "const %s_Table* t;\n" /* op.trait.name */
"} Ref%s;\n\n", /* op.trait.name */
op.trait.name, op.trait.name));
codegen_append_trait_wrapper_structure_some_refkind(&res, op.trait, cstr("Ref"), false);
for (size_t i = 0; i < op.trait.methods.len; i++) {
NamedMethodSignatureRecordRef method = op.trait.methods.data[i];
if (method.takes_mut_self)
@ -109,24 +124,14 @@ NODISCARD VecU8 generate_trait_wrapper_boilerplate(trait_wrapper_boil_options op
}
}
if (op.mut_ref) {
VecU8_append_vec(&res, VecU8_fmt(
"typedef struct {\n"
SPACE "void* r;\n"
SPACE "const %s_Table* t;\n"
"} MutRef%s;\n\n",
op.trait.name, op.trait.name));
codegen_append_trait_wrapper_structure_some_refkind(&res, op.trait, cstr("MutRef"), true);
for (size_t i = 0; i < op.trait.methods.len; i++) {
NamedMethodSignatureRecordRef method = op.trait.methods.data[i];
codegen_append_trait_wrapper_some_method_some_refkind(&res, op.trait.name, method, cstr("MutRef"), false);
}
}
if (op.box) {
VecU8_append_vec(&res, VecU8_fmt(
"typedef struct {\n"
SPACE "void* r;\n"
SPACE "const %s_Table* t;\n"
"} Box%s;\n\n",
op.trait.name, op.trait.name));
codegen_append_trait_wrapper_structure_some_refkind(&res, op.trait, cstr("Box"), true);
for (size_t i = 0; i < op.trait.methods.len; i++) {
NamedMethodSignatureRecordRef method = op.trait.methods.data[i];
codegen_append_trait_wrapper_some_method_some_refkind(&res, op.trait.name, method, cstr("Box"), true);

7
src/l2/gui/label.h Normal file
View File

@ -0,0 +1,7 @@
#ifndef prototype1_src_l2_gui_label_h
#define prototype1_src_l2_gui_label_h
#include "../lucy/glyph_render.h"
#endif

104
src/l2/gui/widget.h Normal file
View File

@ -0,0 +1,104 @@
#ifndef prototype1_src_l2_gui_widget_h
#define prototype1_src_l2_gui_widget_h
#include "../../../gen/l1/geom.h"
#include "../../l1/core/util.h"
#define WIDGET_DIM_INF 1000000
bool is_widget_size_limit_contain_inf(uvec2 max_limits){
return max_limits.x >= WIDGET_DIM_INF || max_limits.y >= WIDGET_DIM_INF;
}
void assert_sane_widget_size(uvec2 sz){
assert(sz.x < WIDGET_DIM_INF || sz.y < WIDGET_DIM_INF);
}
void assert_sane_widget_size_limits(uvec2 max_limits){
assert(max_limits.x <= WIDGET_DIM_INF || max_limits.y <= WIDGET_DIM_INF);
}
typedef struct{
ivec2 lt, rb;
} BorderS32;
bool BorderS32_empty(BorderS32 self){
return self.lt.x >= self.rb.x || self.lt.y >= self.rb.y;
}
BorderS32 BorderS32_intersect(BorderS32 a, BorderS32 b){
return (BorderS32){
{MAX_S32(a.lt.x, b.lt.x), MAX_S32(a.lt.y, b.lt.y)},
{MAX_S32(a.rb.x, b.rb.x), MAX_S32(a.rb.y, b.rb.y)},
};
}
uvec2 widget_size_max_of_all(uvec2 a, uvec2 b){
return (uvec2){MAX_U32(a.x, b.x), MAX_U32(a.y, b.y)};
}
uvec2 widget_size_min_of_all(uvec2 a, uvec2 b){
return (uvec2){MIN_U32(a.x, b.x), MIN_U32(a.y, b.y)};
}
typedef struct {
/* .x == WIDGET_DIM_INF indicates that no preparation was made before the draw operation.
* Calling draw at that state will cause abort. draw operation will reset this flag. This makes framework foolproof.
* Initialize and reset with {WIDGET_DIM_INF, 0} */
uvec2 sz_my_choice;
} Widget;
Widget Widget_new(){
return (Widget){.sz_my_choice = {WIDGET_DIM_INF, 0}};
}
#include "../../../gen/l1_5/eve/gui/Widget.h"
uvec2 MutRefWidget_draw_prepare(MutRefWidget self, uvec2 max_limits){
if (!self.r)
return (uvec2){0};
self.r->sz_my_choice = MutRefWidget_DRAW_PREPARE(self, max_limits);
assert_sane_widget_size(self.r->sz_my_choice);
return self.r->sz_my_choice;
}
void MutRefWidget_draw(MutRefWidget self, ivec2 drawing_offset, uvec2 surface_sz, BorderS32 border){
if (!self.r)
return;
if (self.r->sz_my_choice.x >= WIDGET_DIM_INF)
abortf("Drawing widget before negotiating it's size\n");
MutRefWidget_draw(self, drawing_offset, surface_sz, border);
self.r->sz_my_choice = (uvec2){WIDGET_DIM_INF, 0};
}
/* Some very simple widgets */
typedef struct {
Widget base;
U32 width;
U32 height;
} EmptyWidget;
uvec2 Widget_Table_EmptyWidget_DRAW_PREPARE(Widget* ug, uvec2 limits){
EmptyWidget* self = (EmptyWidget*)ug;
uvec2 R = widget_size_min_of_all((uvec2){self->width, self->height}, limits);
return is_widget_size_limit_contain_inf(R) ? (uvec2){0, 0} : R;
}
void Widget_Table_EmptyWidget_DRAW(Widget* ug, ivec2 drawing_offset, uvec2 surface_sz, BorderS32 border){}
void Widget_Table_EmptyWidget_drop(Widget* ug){}
const Widget_Table Widget_Table_EmptyWidget = {
.DRAW_PREPARE = Widget_Table_EmptyWidget_DRAW_PREPARE,
.DRAW = Widget_Table_EmptyWidget_DRAW,
.drop = Widget_Table_EmptyWidget_drop,
};
BoxWidget EmptyWidget_new_box(U32 width, U32 height){
EmptyWidget* r = safe_malloc(sizeof(EmptyWidget));
*r = (EmptyWidget){.base = Widget_new(), .width = width, .height = height};
return (BoxWidget){.r = (Widget*)r, .t = &Widget_Table_EmptyWidget};
}
#endif

View File

@ -98,5 +98,8 @@ mat4 marie_3d_scal_mat4(float scale){
0, 0, 0, 1);
}
vec2 ivec2_to_vec2(ivec2 v){
return (vec2){(float)v.x, (float)v.y};
}
#endif

View File

@ -1,135 +0,0 @@
#!/usr/bin/env python3
"""
Convert between custom bottomup raw files (.r8g8b8a8 / .r8b8g8 / .r8 / .a8)
and normal PNG using Pillow.
Format
------
uint32 width (littleendian)
uint32 height (littleendian)
pixel data rows bottomfirst:
* .r8g8b8a8 : R G B A (4× uint8)
* .r8b8g8 : R G B (3× uint8)
* .r8 : R (1× uint8 <- grayscale)
* .a8 : A (can convert from it, but can't convert to it)
CLI
---
raw -> png : python raw_png_conv.py to_png input.raw output.png
png -> raw : python raw_png_conv.py to_raw input.png output.raw
"""
import argparse
import struct
from pathlib import Path
from PIL import Image
# --------------------------------------------------------------------- #
# Helpers
# --------------------------------------------------------------------- #
RAW_FORMATS = {
".r8g8b8a8": {"pix_size": 4, "mode": "RGBA"},
".r8g8b8": {"pix_size": 3, "mode": "RGB"},
".r8": {"pix_size": 1, "mode": "L"},
".a8": {"pix_size": 1, "mode": None}, # special-cased (alpha-only)
}
def get_fmt(path: Path):
fmt = RAW_FORMATS.get(path.suffix.lower())
if not fmt:
raise ValueError(f"Unknown raw extension: {path.suffix}")
return fmt
def read_header_and_data(path: Path, pix_size: int):
with path.open("rb") as f:
header = f.read(8)
if len(header) != 8:
raise ValueError("File too short for header")
w, h = struct.unpack("<II", header)
expected = w * h * pix_size
data = f.read()
if len(data) != expected:
raise ValueError(
f"Pixel data length mismatch: expected {expected}, got {len(data)}"
)
return w, h, data
def read_raw(path: Path) -> Image.Image:
"""Load any supported raw file -> Pillow Image."""
spec = get_fmt(path)
w, h, data = read_header_and_data(path, spec["pix_size"])
row_len = w * spec["pix_size"]
rows = [data[i : i + row_len] for i in range(0, len(data), row_len)]
top_down = b"".join(reversed(rows)) # flip bottomup -> topdown
# Special handling for .a8 (alpha-only -> LA with black color)
if path.suffix.lower() == ".a8":
big = bytearray(4 * len(top_down))
big[3::4] = top_down
big = bytes(big)
return Image.frombytes("RGBA", (w, h), big)
# Normal cases can be constructed directly
return Image.frombytes(spec["mode"], (w, h), top_down)
def write_raw(img: Image.Image, path: Path) -> None:
"""Write Pillow Image -> raw file chosen by path suffix."""
ext = path.suffix.lower()
spec = get_fmt(path)
if ext == ".a8":
raise ValueError("Don't convert to .a8 format")
target_mode = spec["mode"]
if img.mode != target_mode:
img = img.convert(target_mode)
w, h = img.size
data = img.tobytes()
row_len = w * spec["pix_size"]
rows = [data[i : i + row_len] for i in range(0, len(data), row_len)]
bottom_first = b"".join(reversed(rows)) # topdown -> bottomup
with path.open("wb") as f:
f.write(struct.pack("<II", w, h))
f.write(bottom_first)
# --------------------------------------------------------------------- #
# CLI
# --------------------------------------------------------------------- #
def to_png(src: Path, dst: Path):
read_raw(src).save(dst, "PNG")
def to_raw(src: Path, dst: Path):
write_raw(Image.open(src), dst)
def main():
ap = argparse.ArgumentParser(description="Convert raw <-> PNG")
sub = ap.add_subparsers(dest="cmd", required=True)
p_png = sub.add_parser("to_png", help="raw -> png")
p_png.add_argument("src", type=Path)
p_png.add_argument("dst", type=Path)
p_raw = sub.add_parser("to_raw", help="png -> raw")
p_raw.add_argument("src", type=Path)
p_raw.add_argument("dst", type=Path)
args = ap.parse_args()
if args.cmd == "to_png":
to_png(args.src, args.dst)
else:
to_raw(args.src, args.dst)
if __name__ == "__main__":
main()