diff --git a/src/l1/codegen/codegen.h b/src/l1/codegen/codegen.h index 6f39f36..367718c 100644 --- a/src/l1/codegen/codegen.h +++ b/src/l1/codegen/codegen.h @@ -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 diff --git a/src/l1_5/anne/codegen.c b/src/l1_5/anne/codegen.c index 9d21a5f..4ffe939 100644 --- a/src/l1_5/anne/codegen.c +++ b/src/l1_5/anne/codegen.c @@ -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; } diff --git a/src/l1_5/anne/gui.h b/src/l1_5/anne/gui.h new file mode 100644 index 0000000..d3889d4 --- /dev/null +++ b/src/l1_5/anne/gui.h @@ -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 \ No newline at end of file diff --git a/src/l1_5/anne/liza.h b/src/l1_5/anne/liza.h index 0a41df6..96f8916 100644 --- a/src/l1_5/anne/liza.h +++ b/src/l1_5/anne/liza.h @@ -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 }); diff --git a/src/l1_5/codegen/trait_wrap_boil.h b/src/l1_5/codegen/trait_wrap_boil.h index 7dd20fc..1b0203a 100644 --- a/src/l1_5/codegen/trait_wrap_boil.h +++ b/src/l1_5/codegen/trait_wrap_boil.h @@ -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); diff --git a/src/l2/gui/label.h b/src/l2/gui/label.h new file mode 100644 index 0000000..e5a9f2c --- /dev/null +++ b/src/l2/gui/label.h @@ -0,0 +1,7 @@ +#ifndef prototype1_src_l2_gui_label_h +#define prototype1_src_l2_gui_label_h + +#include "../lucy/glyph_render.h" + + +#endif \ No newline at end of file diff --git a/src/l2/gui/widget.h b/src/l2/gui/widget.h new file mode 100644 index 0000000..ce259c1 --- /dev/null +++ b/src/l2/gui/widget.h @@ -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 \ No newline at end of file diff --git a/src/l2/marie/graphics_geom.h b/src/l2/marie/graphics_geom.h index 360ba7f..03b9247 100644 --- a/src/l2/marie/graphics_geom.h +++ b/src/l2/marie/graphics_geom.h @@ -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 diff --git a/src/l2/tests/r0/textures/bitmap_converter.py b/src/l2/tests/r0/textures/bitmap_converter.py deleted file mode 100755 index 3c8ab66..0000000 --- a/src/l2/tests/r0/textures/bitmap_converter.py +++ /dev/null @@ -1,135 +0,0 @@ -#!/usr/bin/env python3 - -""" -Convert between custom bottom‑up raw files (.r8g8b8a8 / .r8b8g8 / .r8 / .a8) -and normal PNG using Pillow. - -Format ------- -uint32 width (little‑endian) -uint32 height (little‑endian) -pixel data rows bottom‑first: - * .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(" 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 bottom‑up -> top‑down - - # 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)) # top‑down -> bottom‑up - - with path.open("wb") as f: - f.write(struct.pack(" 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()