From 85ef56f657ae510391bee99901af56363d368627 Mon Sep 17 00:00:00 2001 From: Joseph Ferano Date: Sun, 13 Sep 2026 08:07:40 +0700 Subject: [PATCH] The bindings are committed, and regeneration is what checks them MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit generated.flan carries the 253 declarations the importer reads out of raylib's header, so a build needs libraylib linkable and no header at all. The opt-in no longer decides how many bindings a package has — every build now gets all 425, they are greppable, and they diff when raylib moves. What that gives up is the build-time check, so `flan generate-c` is the only thing that writes the file and it compares first: every defstruct against the header's record, every hand-written declare-c against the header's signature, and it writes nothing when they disagree. Against the 5.1-dev header on this machine that is ten real differences and no write. The 172 hand-written lines stay, and not out of caution. Everything the generator emits agrees with the header by construction, so diffing generated output against its own source is a tautology; the hand-written lines were transcribed by a person, so they are the only thing here a header can contradict. All ten of those differences came from them. `bindings` beside `headers` is what survives regeneration, because a hand-edit to a committed generated file does not. Two directives: `exclude` drops raylib's three allocator entry points, and `name` gives the 19 generated predicates the `?` spelling the hand-written ones already use. --- bin/main.ml | 76 ++++++++++ lib/cimport.ml | 246 +++++++++++++++++++++++++++++++- lib/load.ml | 12 +- test/test_flan.ml | 2 +- vendor/raylib/bindings | 69 +++++++++ vendor/raylib/generated.flan | 267 +++++++++++++++++++++++++++++++++++ vendor/raylib/headers | 50 +++++-- 7 files changed, 698 insertions(+), 24 deletions(-) create mode 100644 vendor/raylib/bindings create mode 100644 vendor/raylib/generated.flan diff --git a/bin/main.ml b/bin/main.ml index 25b9a04..57d0dd1 100644 --- a/bin/main.ml +++ b/bin/main.ml @@ -208,6 +208,10 @@ let () = let imported, dump, env = Flan.Cimport.header ~loc:(Flan.Loc.make header 0 0) ~header ~flags ~known_structs:(List.map fst structs) ~known_enums ~taken ~bound_syms + ~config: + (match pkg with + | f :: _ -> Flan.Load.binding_config (Filename.dirname f) + | [] -> Flan.Cimport.no_config) in List.iter (fun d -> print_endline (Flan.Cimport.decl_source d)) @@ -254,6 +258,77 @@ let () = x.Flan.Cimport.dflan x.Flan.Cimport.dsym x.Flan.Cimport.dwhy) ds) + (* Regeneration. [import-c] prints what it would produce; this writes it, and + the difference between the two is that this one cannot skip the check. + + It reads the header out of the package's own `headers`, not off the + command line, because the version that may be read is a property of the + package — `headers` is where it says which one, and `link` is where it + says which library that has to match. And it reads every .flan in the + directory *except* the file it writes, so the hand-written declarations + still win and the generated ones are not mistaken for them on the next + run. + + Non-zero and nothing written when the package and the header disagree. + That is the whole point: the committed file is the one thing here with no + second opinion, so the moment of writing it is the only moment left at + which the library can contradict it. *) + | _ :: "generate-c" :: dir :: _ -> + with_errors dir (fun () -> + let loc = Flan.Loc.make dir 0 0 in + let out = Filename.concat dir "generated.flan" in + let ds = + List.concat_map + (fun f -> Flan.Parse.program (Flan.Reader.read_file f)) + (List.filter + (fun f -> not (String.equal f out)) + (Flan.Load.entries dir ".flan")) + in + let config = Flan.Load.binding_config dir in + match Flan.Load.header_specs ~loc dir with + | [] -> + Printf.eprintf + "flan generate-c: %s/headers names no header that is there. \ + Regeneration reads the library's own header, at the version \ + %s/link names — export it and run this again.\n" dir dir; + exit 2 + | _ :: _ :: _ -> + Printf.eprintf + "flan generate-c: %s/headers names more than one header, and one \ + generated file cannot come from several — the declarations would \ + depend on which was read last.\n" dir; + exit 2 + | [ (h, flags) ] -> + let r = Flan.Cimport.regenerate ~loc ~header:h ~flags ~ds ~config ~out in + List.iter + (fun (n, why) -> Printf.printf ";; refused %s: %s\n" n why) + r.Flan.Cimport.ghidden; + List.iter + (fun (n, why) -> Printf.eprintf "DISAGREES %s: %s\n" n why) + r.Flan.Cimport.gstructs; + List.iter + (fun (x : Flan.Cimport.sig_diff) -> + Printf.eprintf "DIFFERS %s (%s): %s\n" + x.Flan.Cimport.dflan x.Flan.Cimport.dsym x.Flan.Cimport.dwhy) + r.Flan.Cimport.gsigs; + if r.Flan.Cimport.gwrote then + Printf.printf + "wrote %s: %d declarations, %d refused, of %d functions in %s.\n\ + Every defstruct and every hand-written declare-c agrees with it.\n" + out r.Flan.Cimport.gdecls + (List.length r.Flan.Cimport.ghidden) r.Flan.Cimport.gfns h + else begin + Printf.eprintf + "flan generate-c: %s disagrees with %s — %d struct layouts and %d \ + hand-written signatures. Nothing was written: a generated file \ + made against a header the library does not match is the silent \ + failure this check exists to prevent.\n" + dir h + (List.length r.Flan.Cimport.gstructs) + (List.length r.Flan.Cimport.gsigs); + exit 1 + end) + (* The IR is target-independent — [Emit] writes no triple and no datalayout, which is what lets one .ll serve both targets — so there is nothing for a target to change here. Refused rather than accepted and ignored: silently @@ -398,6 +473,7 @@ let () = prerr_endline "usage: flan (read|parse|check|emit|shim) ...\n\ \ flan import-c [package.flan...] [clang flags...]\n\ + \ flan generate-c \n\ \ flan build [-o out] [--no-bounds-checks] [--dev] \ [--debug] [--sanitize] [--target=wasm32-wasi|web]\n\ \ flan run [args...]\n\ diff --git a/lib/cimport.ml b/lib/cimport.ml index a251adb..387f5d4 100644 --- a/lib/cimport.ml +++ b/lib/cimport.ml @@ -502,6 +502,102 @@ type imported = { hidden : (string * string) list; } +(* ── The config beside the header ──────────────────────────────────── *) + +(* Why a config file exists at all, when the importer decides everything else + from the header. + + Because the generated declarations are *committed*. A generated file that is + read at build time can be hand-corrected and the correction survives, since + nothing rewrites it. A generated file that is checked in is rewritten by the + next regeneration, so a hand-edit to it is destroyed without anybody being + told — which is the worst shape an edit can have. The edits therefore have + to live somewhere regeneration *reads* rather than somewhere it writes, and + this is that place. + + Two directives, which are the two things the header cannot decide: + + - [exclude], a C symbol or a pattern of them, for what should not be + generated at all. + - [name], a C symbol and the Flan name it is to take, for where the kebab + rule gives something ugly. + + A postprocessing transform pass over the generated file was the alternative + and was rejected: a second program to understand, run over text the + generator had already committed to. Both directives here are applied + *while* the declarations are made, so the file on disk is already what the + config says and nothing reads it twice. *) + +type config = { + excludes : string list; + (* C symbol to Flan name. Keyed on the symbol and not on the kebab result, + because the symbol is the only spelling that is stable — the whole point + of an override is that the kebab result is not what is wanted. *) + renames : (string * string) list; +} + +let no_config = { excludes = []; renames = [] } + +(* [*] stands for any run of characters and nothing else does anything. Enough + for [rl*] or [*Callback], and small enough to read at a glance; a package + that needs more than this wants a hand-written declare-c, which it has. *) +let matches (pat : string) (s : string) = + let np = String.length pat and ns = String.length s in + let rec go i j = + if i = np then j = ns + else if pat.[i] = '*' then + let rec from k = (k <= ns && go (i + 1) k) || (k < ns && from (k + 1)) in + from j + else j < ns && Char.equal pat.[i] s.[j] && go (i + 1) (j + 1) + in + go 0 0 + +let excluded cfg sym = List.exists (fun p -> matches p sym) cfg.excludes + +(* The one place the kebab rule is consulted, so an override is not a special + case anywhere below: collisions are computed on the name a function will + actually take, which means renaming one of two colliding symbols resolves + the collision rather than leaving both refused. *) +let flan_name cfg sym = + match List.assoc_opt sym cfg.renames with Some n -> n | None -> kebab sym + +(* The file, in the shape of [headers] and [link] beside it: one directive per + line, [#] comments, blank lines ignored. A line that is neither directive is + an error rather than a line quietly skipped — the house rule against + swallowing things applies to a config as much as to a flag, and a typo in a + name override would otherwise show up as a binding under the wrong name. *) +let read_config path : config = + if not (Sys.file_exists path) then no_config + else begin + let ch = open_in path in + let excludes = ref [] and renames = ref [] in + let rec go n = + match input_line ch with + | line -> + let t = String.trim line in + if t <> "" && t.[0] <> '#' then begin + let ws = + String.split_on_char ' ' t + |> List.concat_map (String.split_on_char '\t') + |> List.filter (fun w -> w <> "") + in + match ws with + | [ "exclude"; p ] -> excludes := p :: !excludes + | [ "name"; sym; flan ] -> renames := (sym, flan) :: !renames + | _ -> + close_in ch; + fail (Loc.make path n 0) + "a line here is `exclude ` or `name `, and this is neither: %s" t + end; + go (n + 1) + | exception End_of_file -> () + in + go 1; + close_in ch; + { excludes = List.rev !excludes; renames = List.rev !renames } + end + (* [taken] is every name the package already declares, which is what makes the sidecar additive: a hand-written [(declare-c get-gamepad-name ...)] wins over the header, and a C symbol already bound by hand is not bound twice — @@ -510,7 +606,7 @@ type imported = { [-c] as well as the name itself, because [Shim] generates [foo-c] beside a [foo] whose signature has a struct in it, and a collision there is refused for the whole program rather than for the one binding. *) -let of_dump ~env ~taken ~bound_syms (d : dump) : imported = +let of_dump ~env ~taken ~bound_syms ~config (d : dump) : imported = let decls = ref [] and hidden = ref [] in (* Collisions are found before anything is emitted, and they take *every* name in the colliding group down with them. @@ -530,16 +626,32 @@ let of_dump ~env ~taken ~bound_syms (d : dump) : imported = let candidates = List.filter (fun f -> not (List.mem f.csym bound_syms)) d.fns in + (* Excluded before anything else looks at them, so an excluded symbol is not + in a collision group either — which is one of the things exclusion is for. + It still says why, under the name it would have taken: "there is no such + binding" and "the package decided against this binding" are different + answers and a reader deserves the second one. *) + let dropped, candidates = + List.partition (fun f -> excluded config f.csym) candidates + in + List.iter + (fun f -> + hidden := + (flan_name config f.csym, + Printf.sprintf + "%s is excluded by the package's binding config, so no declaration is generated for it" f.csym) + :: !hidden) + dropped; let groups = Hashtbl.create 512 in List.iter (fun f -> - let k = kebab f.csym in + let k = flan_name config f.csym in Hashtbl.replace groups k (f.csym :: Option.value ~default:[] (Hashtbl.find_opt groups k))) candidates; List.iter (fun f -> - let flan = kebab f.csym in + let flan = flan_name config f.csym in let skip why = hidden := (flan, why) :: !hidden in match List.rev (Hashtbl.find groups flan) with | _ :: _ :: _ as all -> @@ -547,7 +659,8 @@ let of_dump ~env ~taken ~bound_syms (d : dump) : imported = (Printf.sprintf "%s all kebab to %s, and which one got the name would depend on \ the order the header declares them in — so none of them takes \ - it. Bind the one you want with a hand-written declare-c" + it. Give the one you want a `name` in the binding config, or \ + bind it with a hand-written declare-c" (String.concat ", " all) flan) | _ -> if Hashtbl.mem taken flan || Hashtbl.mem taken (flan ^ "-c") then @@ -811,7 +924,8 @@ let dump_of ~loc ~header ~flags = let env_of ~known_structs ~known_enums d = { known_structs; known_enums; d } -let header ~loc ~header:h ~flags ~known_structs ~known_enums ~taken ~bound_syms = +let header ~loc ~header:h ~flags ~known_structs ~known_enums ~taken ~bound_syms + ~config = let k = (* Sorted, because neither the taken table nor the declaration order is a fact about the package — two loads of the same file that enumerate them @@ -822,14 +936,19 @@ let header ~loc ~header:h ~flags ~known_structs ~known_enums ~taken ~bound_syms :: "\001" :: sorted known_structs @ ("\001" :: sorted known_enums) @ ("\001" :: sorted (Hashtbl.fold (fun n () acc -> n :: acc) taken [])) - @ ("\001" :: sorted bound_syms)) + @ ("\001" :: sorted bound_syms) + (* The config is part of the question: two loads that disagree about + what is excluded or renamed are different questions, and serving one + the other's answer is the bug this key exists to prevent. *) + @ ("\001" :: sorted config.excludes) + @ ("\001" :: sorted (List.map (fun (a, b) -> a ^ "=" ^ b) config.renames))) in match Hashtbl.find_opt imports k with | Some r -> r | None -> let d = dump_of ~loc ~header:h ~flags in let env = env_of ~known_structs ~known_enums d in - let r = (of_dump ~env ~taken ~bound_syms d, d, env) in + let r = (of_dump ~env ~taken ~bound_syms ~config d, d, env) in Hashtbl.replace imports k r; r @@ -955,3 +1074,116 @@ let diff_bound ~env ~(bound : (Ast.fn * string) list) (d : dump) = (match want with None -> "nothing" | Some t -> ty_source t) c.cret))) bound + + +(* ── Regenerating the committed declarations ───────────────────────── *) + +(* Generate once, commit the result, regenerate when the library moves. + + What that buys is in DISCUSS.md item 6 and it is not caching — the dump is + already cached on disk and in memory, so a build that reads the header pays + for it once either way. It is that no header is needed by *anybody*: the + declarations are in the repository, so they are greppable, they diff when + raylib moves, and a build needs libraylib linkable and nothing else. The + opt-in that used to decide whether a package had 172 bindings or 428 stops + deciding anything. + + What it costs is the check. A header read at build time compared every + hand-written declaration against the library on every build; a committed + file compares nothing, because a file on disk has no second opinion. That + check is not decoration — it verified all 172 hand-written declarations and + all 16 struct layouts against raylib 5.5 and found them exactly right, and + against a 5.1-dev header on the same machine it found ten real differences. + + So regeneration runs it, and the check *gates the write*. There is no way to + ask for new declarations without comparing the package against the header + they come from, because the one function that writes the file is this one + and it refuses when the two disagree. A regeneration that quietly rewrote + the bindings against a header the library does not match would produce + exactly the failure BUILT.md warns about — a permuted struct read as five + plausible numbers rather than as a link error — and it would produce it in a + committed file that looks reviewed. + + The hand-written declarations are what make the signature half of that check + mean anything, which is why they stay. Everything the generator emits agrees + with the header by construction, so diffing generated output against the + header it came from is a tautology; the hand-written lines were transcribed + from raylib's documentation by a person, so they are an independent second + opinion and the only thing here that the header can actually contradict. *) + +type regen = { + gwrote : bool; + gdecls : int; + gfns : int; + ghidden : (string * string) list; + gstructs : (string * string) list; + gsigs : sig_diff list; +} + +let banner h = + Printf.sprintf + ";;;; Generated from %s by `flan generate-c`. Do not edit this file.\n\ + ;;;;\n\ + ;;;; Every line here was read out of the C header named by `headers`, and\n\ + ;;;; the next regeneration overwrites the file — so a correction made here\n\ + ;;;; is destroyed without anybody being told. Corrections go in `bindings`\n\ + ;;;; beside it, which is read *while* these lines are made: `exclude` drops\n\ + ;;;; a function, `name` gives one a Flan name the kebab rule would not.\n\ + ;;;; Anything neither directive can express is a hand-written declare-c in\n\ + ;;;; the package's own .flan, which wins over this file and is left alone.\n\ + ;;;;\n\ + ;;;; Regenerating compares the package against the header first and\n\ + ;;;; refuses to write when they disagree, so this file and the\n\ + ;;;; hand-written declarations beside it agreed with %s when it was made.\n\n" + (Filename.basename h) (Filename.basename h) + +(* [ds] is the package's *hand-written* declarations: every .flan in the + directory except the one being written. Reading the output back in would + make regeneration idempotent in the worst way — every symbol would already + be bound, so the second run would generate nothing and cheerfully write an + empty file. *) +let regenerate ~loc ~header:h ~flags ~(ds : Ast.decl list) ~config ~out = + let taken = Hashtbl.create 64 in + List.iter + (fun d -> + match Ast.declared_name d with + | Some n -> Hashtbl.replace taken n () + | None -> ()) + ds; + let pick f = List.filter_map f ds in + let structs = + pick (fun (d : Ast.decl) -> + match d.Ast.d with Ast.Defstruct (n, fs) -> Some (n, fs) | _ -> None) + and known_enums = + pick (fun (d : Ast.decl) -> + match d.Ast.d with Ast.Defenum (n, _) -> Some n | _ -> None) + and bound_syms = + pick (fun (d : Ast.decl) -> + match d.Ast.d with + | Ast.Declare (_, s) | Ast.DeclareC (_, s) -> Some s + | _ -> None) + and bound = + pick (fun (d : Ast.decl) -> + match d.Ast.d with Ast.DeclareC (fn, s) -> Some (fn, s) | _ -> None) + in + let imported, dump, env = + header ~loc ~header:h ~flags ~known_structs:(List.map fst structs) + ~known_enums ~taken ~bound_syms ~config + in + let gstructs = check_structs ~env ~structs dump in + let gsigs = diff_bound ~env ~bound dump in + let gwrote = gstructs = [] && gsigs = [] in + if gwrote then begin + let b = Buffer.create 65536 in + Buffer.add_string b (banner h); + List.iter + (fun d -> + Buffer.add_string b (decl_source d); + Buffer.add_char b '\n') + imported.decls; + let ch = open_out out in + output_string ch (Buffer.contents b); + close_out ch + end; + { gwrote; gdecls = List.length imported.decls; + gfns = List.length dump.fns; ghidden = imported.hidden; gstructs; gsigs } diff --git a/lib/load.ml b/lib/load.ml index 4ed981e..8fd7ebe 100644 --- a/lib/load.ml +++ b/lib/load.ml @@ -587,6 +587,16 @@ let header_specs ~loc dir = else Some (h, flags))) (read_lines path) +(* The binding config, beside [headers] and read for the same package: which + functions not to generate, and what to call the ones whose kebab name is + not wanted. Absent is the ordinary case and means neither. + + It is read here rather than by the importer because it is a property of the + *package*, like [headers] and [link] — the importer is given a header and a + config and has no directory to look in. See [Cimport.read_config] for why a + config exists at all once the generated declarations are committed. *) +let binding_config dir = Cimport.read_config (Filename.concat dir "bindings") + let real dir = try Unix.realpath dir with Unix.Unix_error _ -> dir (* One package, and whatever it imports. @@ -758,7 +768,7 @@ let rec import ~seen ~open_ ~loc alias dir = in let r, dump, env = Cimport.header ~loc ~header:h ~flags ~known_structs ~known_enums - ~taken ~bound_syms + ~taken ~bound_syms ~config:(binding_config dir) in (* The point of reading the header, and the reason it is not enough to generate declarations out of it. diff --git a/test/test_flan.ml b/test/test_flan.ml index bca099d..a6c7b8d 100644 --- a/test/test_flan.ml +++ b/test/test_flan.ml @@ -1318,7 +1318,7 @@ let () = in let i, d, e = Cimport.header ~loc:Loc.unknown ~header:"headers/sample.h" ~flags:[] - ~known_structs ~known_enums ~taken ~bound_syms:[] + ~known_structs ~known_enums ~taken ~bound_syms:[] ~config:Cimport.no_config in (i, d, e, ds) in diff --git a/vendor/raylib/bindings b/vendor/raylib/bindings new file mode 100644 index 0000000..5dd7498 --- /dev/null +++ b/vendor/raylib/bindings @@ -0,0 +1,69 @@ +# How the generated bindings in generated.flan are shaped. One directive per +# line, `#` comments, blank lines ignored. A line that is neither directive is +# an error rather than a line quietly skipped. +# +# exclude do not generate a binding for this +# name call it this instead of the kebab rule's +# +# `*` in an exclude pattern stands for any run of characters, and nothing else +# does anything. +# +# Why this file exists. generated.flan is committed, so it is not a build +# artefact anybody can hand-correct: the next `flan generate-c vendor/raylib` +# overwrites it, and an edit made there is destroyed without anybody being +# told. This file is read *while* those lines are made, so it is the edit that +# survives regeneration. A postprocessing pass over the generated text was the +# alternative and was rejected — a second program to understand, applied to +# output the generator had already committed to. +# +# What does NOT belong here. Anything neither directive can express is a +# hand-written `declare-c` in raylib.flan, which wins over the generated file +# and is left alone by the importer. That is the escape hatch for a signature +# the importer gets wrong and for a Flan face the header cannot describe — +# load-font-ex-raw and load-image-from-memory-raw are both that, each wrapped +# by a Flan defn of the same name without the suffix. + +# ── raylib's allocator ────────────────────────────────────────────── +# +# raylib exports malloc, realloc and free under its own names. Flan's memory +# model is explicit — plan.org's rule is that an operation never falls back to +# a hidden allocator — so binding these would put a second, untracked heap +# behind three innocuous-looking Flan names, and a pointer from one freed by +# the other is the kind of bug that does not point at itself. Nothing in the +# package needs them: raylib's own Load*/Unload* pairs own everything raylib +# allocates. +exclude MemAlloc +exclude MemRealloc +exclude MemFree + +# ── Predicates read as questions ──────────────────────────────────── +# +# The kebab rule gives `is-window-ready`, which is C's phrasing wearing Flan's +# punctuation. The hand-written bindings in raylib.flan settled the convention +# years of Lisp settled first — a predicate ends in `?` and does not begin with +# `is` — and these are the generated half of the same set, so they follow it. +# key-pressed? and window-ready? being spelled differently would be the split +# this whole change exists to remove. +# +# The C symbol is kept verbatim in the declaration either way, so nothing here +# is lost: the name is the Flan face and `IsWindowReady` is still what is +# called and still what the check compares against. +name IsWindowReady window-ready? +name IsWindowFullscreen window-fullscreen? +name IsWindowHidden window-hidden? +name IsWindowMinimized window-minimized? +name IsWindowMaximized window-maximized? +name IsWindowFocused window-focused? +name IsWindowResized window-resized? +name IsWindowState window-state? +name IsCursorOnScreen cursor-on-screen? +name IsKeyUp key-up? +name IsKeyPressedRepeat key-pressed-repeat? +name IsMouseButtonUp mouse-button-up? +name IsFileDropped file-dropped? +name IsFileExtension file-extension? +name IsFileNameValid file-name-valid? +name IsPathFile path-file? +name IsAudioStreamValid audio-stream-valid? +name IsAudioStreamPlaying audio-stream-playing? +name IsAudioStreamProcessed audio-stream-processed? diff --git a/vendor/raylib/generated.flan b/vendor/raylib/generated.flan new file mode 100644 index 0000000..981c334 --- /dev/null +++ b/vendor/raylib/generated.flan @@ -0,0 +1,267 @@ +;;;; Generated from raylib.h by `flan generate-c`. Do not edit this file. +;;;; +;;;; Every line here was read out of the C header named by `headers`, and +;;;; the next regeneration overwrites the file — so a correction made here +;;;; is destroyed without anybody being told. Corrections go in `bindings` +;;;; beside it, which is read *while* these lines are made: `exclude` drops +;;;; a function, `name` gives one a Flan name the kebab rule would not. +;;;; Anything neither directive can express is a hand-written declare-c in +;;;; the package's own .flan, which wins over this file and is left alone. +;;;; +;;;; Regenerating compares the package against the header first and +;;;; refuses to write when they disagree, so this file and the +;;;; hand-written declarations beside it agreed with raylib.h when it was made. + +(declare-c window-ready? [] bool "IsWindowReady") +(declare-c window-fullscreen? [] bool "IsWindowFullscreen") +(declare-c window-hidden? [] bool "IsWindowHidden") +(declare-c window-minimized? [] bool "IsWindowMinimized") +(declare-c window-maximized? [] bool "IsWindowMaximized") +(declare-c window-focused? [] bool "IsWindowFocused") +(declare-c window-resized? [] bool "IsWindowResized") +(declare-c window-state? [flag u32] bool "IsWindowState") +(declare-c set-window-state [flags u32] "SetWindowState") +(declare-c clear-window-state [flags u32] "ClearWindowState") +(declare-c toggle-fullscreen [] "ToggleFullscreen") +(declare-c toggle-borderless-windowed [] "ToggleBorderlessWindowed") +(declare-c maximize-window [] "MaximizeWindow") +(declare-c minimize-window [] "MinimizeWindow") +(declare-c restore-window [] "RestoreWindow") +(declare-c set-window-icon [image Image] "SetWindowIcon") +(declare-c set-window-icons [images (Ptr Image) count i32] "SetWindowIcons") +(declare-c set-window-title [title string] "SetWindowTitle") +(declare-c set-window-position [x i32 y i32] "SetWindowPosition") +(declare-c set-window-monitor [monitor i32] "SetWindowMonitor") +(declare-c set-window-min-size [width i32 height i32] "SetWindowMinSize") +(declare-c set-window-max-size [width i32 height i32] "SetWindowMaxSize") +(declare-c set-window-size [width i32 height i32] "SetWindowSize") +(declare-c set-window-opacity [opacity f32] "SetWindowOpacity") +(declare-c set-window-focused [] "SetWindowFocused") +(declare-c get-window-handle [] (Ptr u8) "GetWindowHandle") +(declare-c get-render-width [] i32 "GetRenderWidth") +(declare-c get-render-height [] i32 "GetRenderHeight") +(declare-c get-monitor-count [] i32 "GetMonitorCount") +(declare-c get-current-monitor [] i32 "GetCurrentMonitor") +(declare-c get-monitor-position [monitor i32] Vector2 "GetMonitorPosition") +(declare-c get-monitor-width [monitor i32] i32 "GetMonitorWidth") +(declare-c get-monitor-height [monitor i32] i32 "GetMonitorHeight") +(declare-c get-monitor-physical-width [monitor i32] i32 "GetMonitorPhysicalWidth") +(declare-c get-monitor-physical-height [monitor i32] i32 "GetMonitorPhysicalHeight") +(declare-c get-monitor-refresh-rate [monitor i32] i32 "GetMonitorRefreshRate") +(declare-c get-window-position [] Vector2 "GetWindowPosition") +(declare-c get-window-scale-dpi [] Vector2 "GetWindowScaleDPI") +(declare-c set-clipboard-text [text string] "SetClipboardText") +(declare-c get-clipboard-image [] Image "GetClipboardImage") +(declare-c enable-event-waiting [] "EnableEventWaiting") +(declare-c disable-event-waiting [] "DisableEventWaiting") +(declare-c enable-cursor [] "EnableCursor") +(declare-c disable-cursor [] "DisableCursor") +(declare-c cursor-on-screen? [] bool "IsCursorOnScreen") +(declare-c end-mode-3d [] "EndMode3D") +(declare-c end-shader-mode [] "EndShaderMode") +(declare-c begin-blend-mode [mode i32] "BeginBlendMode") +(declare-c end-blend-mode [] "EndBlendMode") +(declare-c begin-scissor-mode [x i32 y i32 width i32 height i32] "BeginScissorMode") +(declare-c end-scissor-mode [] "EndScissorMode") +(declare-c end-vr-stereo-mode [] "EndVrStereoMode") +(declare-c swap-screen-buffer [] "SwapScreenBuffer") +(declare-c poll-input-events [] "PollInputEvents") +(declare-c wait-time [seconds f64] "WaitTime") +(declare-c set-random-seed [seed u32] "SetRandomSeed") +(declare-c get-random-value [min i32 max i32] i32 "GetRandomValue") +(declare-c load-random-sequence [count u32 min i32 max i32] (Ptr i32) "LoadRandomSequence") +(declare-c unload-random-sequence [sequence (Ptr i32)] "UnloadRandomSequence") +(declare-c take-screenshot [file-name string] "TakeScreenshot") +(declare-c open-url [url string] "OpenURL") +(declare-c load-file-data [file-name string data-size (Ptr i32)] (Ptr u8) "LoadFileData") +(declare-c unload-file-data [data (Ptr u8)] "UnloadFileData") +(declare-c save-file-data [file-name string data (Ptr u8) data-size i32] bool "SaveFileData") +(declare-c export-data-as-code [data (Ptr u8) data-size i32 file-name string] bool "ExportDataAsCode") +(declare-c file-exists [file-name string] bool "FileExists") +(declare-c directory-exists [dir-path string] bool "DirectoryExists") +(declare-c file-extension? [file-name string ext string] bool "IsFileExtension") +(declare-c get-file-length [file-name string] i32 "GetFileLength") +(declare-c make-directory [dir-path string] i32 "MakeDirectory") +(declare-c change-directory [dir string] bool "ChangeDirectory") +(declare-c path-file? [path string] bool "IsPathFile") +(declare-c file-name-valid? [file-name string] bool "IsFileNameValid") +(declare-c file-dropped? [] bool "IsFileDropped") +(declare-c compress-data [data (Ptr u8) data-size i32 comp-data-size (Ptr i32)] (Ptr u8) "CompressData") +(declare-c decompress-data [comp-data (Ptr u8) comp-data-size i32 data-size (Ptr i32)] (Ptr u8) "DecompressData") +(declare-c decode-data-base-64 [data (Ptr u8) output-size (Ptr i32)] (Ptr u8) "DecodeDataBase64") +(declare-c compute-crc32 [data (Ptr u8) data-size i32] u32 "ComputeCRC32") +(declare-c compute-md5 [data (Ptr u8) data-size i32] (Ptr u32) "ComputeMD5") +(declare-c compute-sha1 [data (Ptr u8) data-size i32] (Ptr u32) "ComputeSHA1") +(declare-c set-automation-event-base-frame [frame i32] "SetAutomationEventBaseFrame") +(declare-c start-automation-event-recording [] "StartAutomationEventRecording") +(declare-c stop-automation-event-recording [] "StopAutomationEventRecording") +(declare-c key-pressed-repeat? [key i32] bool "IsKeyPressedRepeat") +(declare-c key-up? [key i32] bool "IsKeyUp") +(declare-c get-key-pressed [] i32 "GetKeyPressed") +(declare-c get-char-pressed [] i32 "GetCharPressed") +(declare-c set-exit-key [key i32] "SetExitKey") +(declare-c set-gamepad-mappings [mappings string] i32 "SetGamepadMappings") +(declare-c set-gamepad-vibration [gamepad i32 left-motor f32 right-motor f32 duration f32] "SetGamepadVibration") +(declare-c mouse-button-up? [button i32] bool "IsMouseButtonUp") +(declare-c get-mouse-x [] i32 "GetMouseX") +(declare-c get-mouse-y [] i32 "GetMouseY") +(declare-c get-mouse-delta [] Vector2 "GetMouseDelta") +(declare-c set-mouse-position [x i32 y i32] "SetMousePosition") +(declare-c set-mouse-offset [offset-x i32 offset-y i32] "SetMouseOffset") +(declare-c set-mouse-scale [scale-x f32 scale-y f32] "SetMouseScale") +(declare-c get-mouse-wheel-move-v [] Vector2 "GetMouseWheelMoveV") +(declare-c set-mouse-cursor [cursor i32] "SetMouseCursor") +(declare-c draw-line-strip [points (Ptr Vector2) point-count i32 color Color] "DrawLineStrip") +(declare-c draw-line-bezier [start-pos Vector2 end-pos Vector2 thick f32 color Color] "DrawLineBezier") +(declare-c draw-circle-sector [center Vector2 radius f32 start-angle f32 end-angle f32 segments i32 color Color] "DrawCircleSector") +(declare-c draw-circle-sector-lines [center Vector2 radius f32 start-angle f32 end-angle f32 segments i32 color Color] "DrawCircleSectorLines") +(declare-c draw-circle-gradient [center-x i32 center-y i32 radius f32 inner Color outer Color] "DrawCircleGradient") +(declare-c draw-rectangle-pro [rec Rectangle origin Vector2 rotation f32 color Color] "DrawRectanglePro") +(declare-c draw-rectangle-gradient-v [pos-x i32 pos-y i32 width i32 height i32 top Color bottom Color] "DrawRectangleGradientV") +(declare-c draw-rectangle-gradient-h [pos-x i32 pos-y i32 width i32 height i32 left Color right Color] "DrawRectangleGradientH") +(declare-c draw-rectangle-gradient-ex [rec Rectangle top-left Color bottom-left Color top-right Color bottom-right Color] "DrawRectangleGradientEx") +(declare-c draw-triangle-fan [points (Ptr Vector2) point-count i32 color Color] "DrawTriangleFan") +(declare-c draw-triangle-strip [points (Ptr Vector2) point-count i32 color Color] "DrawTriangleStrip") +(declare-c draw-poly [center Vector2 sides i32 radius f32 rotation f32 color Color] "DrawPoly") +(declare-c draw-poly-lines [center Vector2 sides i32 radius f32 rotation f32 color Color] "DrawPolyLines") +(declare-c draw-poly-lines-ex [center Vector2 sides i32 radius f32 rotation f32 line-thick f32 color Color] "DrawPolyLinesEx") +(declare-c draw-spline-linear [points (Ptr Vector2) point-count i32 thick f32 color Color] "DrawSplineLinear") +(declare-c draw-spline-basis [points (Ptr Vector2) point-count i32 thick f32 color Color] "DrawSplineBasis") +(declare-c draw-spline-catmull-rom [points (Ptr Vector2) point-count i32 thick f32 color Color] "DrawSplineCatmullRom") +(declare-c draw-spline-bezier-quadratic [points (Ptr Vector2) point-count i32 thick f32 color Color] "DrawSplineBezierQuadratic") +(declare-c draw-spline-bezier-cubic [points (Ptr Vector2) point-count i32 thick f32 color Color] "DrawSplineBezierCubic") +(declare-c draw-spline-segment-linear [p-1 Vector2 p-2 Vector2 thick f32 color Color] "DrawSplineSegmentLinear") +(declare-c draw-spline-segment-basis [p-1 Vector2 p-2 Vector2 p-3 Vector2 p-4 Vector2 thick f32 color Color] "DrawSplineSegmentBasis") +(declare-c draw-spline-segment-catmull-rom [p-1 Vector2 p-2 Vector2 p-3 Vector2 p-4 Vector2 thick f32 color Color] "DrawSplineSegmentCatmullRom") +(declare-c draw-spline-segment-bezier-quadratic [p-1 Vector2 c-2 Vector2 p-3 Vector2 thick f32 color Color] "DrawSplineSegmentBezierQuadratic") +(declare-c draw-spline-segment-bezier-cubic [p-1 Vector2 c-2 Vector2 c-3 Vector2 p-4 Vector2 thick f32 color Color] "DrawSplineSegmentBezierCubic") +(declare-c get-spline-point-linear [start-pos Vector2 end-pos Vector2 t f32] Vector2 "GetSplinePointLinear") +(declare-c get-spline-point-basis [p-1 Vector2 p-2 Vector2 p-3 Vector2 p-4 Vector2 t f32] Vector2 "GetSplinePointBasis") +(declare-c get-spline-point-catmull-rom [p-1 Vector2 p-2 Vector2 p-3 Vector2 p-4 Vector2 t f32] Vector2 "GetSplinePointCatmullRom") +(declare-c get-spline-point-bezier-quad [p-1 Vector2 c-2 Vector2 p-3 Vector2 t f32] Vector2 "GetSplinePointBezierQuad") +(declare-c get-spline-point-bezier-cubic [p-1 Vector2 c-2 Vector2 c-3 Vector2 p-4 Vector2 t f32] Vector2 "GetSplinePointBezierCubic") +(declare-c load-image-raw [file-name string width i32 height i32 format i32 header-size i32] Image "LoadImageRaw") +(declare-c load-image-anim [file-name string frames (Ptr i32)] Image "LoadImageAnim") +(declare-c load-image-anim-from-memory [file-type string file-data (Ptr u8) data-size i32 frames (Ptr i32)] Image "LoadImageAnimFromMemory") +(declare-c load-image-from-texture [texture Texture2D] Image "LoadImageFromTexture") +(declare-c load-image-from-screen [] Image "LoadImageFromScreen") +(declare-c export-image-to-memory [image Image file-type string file-size (Ptr i32)] (Ptr u8) "ExportImageToMemory") +(declare-c export-image-as-code [image Image file-name string] bool "ExportImageAsCode") +(declare-c gen-image-gradient-linear [width i32 height i32 direction i32 start Color end Color] Image "GenImageGradientLinear") +(declare-c gen-image-gradient-radial [width i32 height i32 density f32 inner Color outer Color] Image "GenImageGradientRadial") +(declare-c gen-image-gradient-square [width i32 height i32 density f32 inner Color outer Color] Image "GenImageGradientSquare") +(declare-c gen-image-checked [width i32 height i32 checks-x i32 checks-y i32 col-1 Color col-2 Color] Image "GenImageChecked") +(declare-c gen-image-white-noise [width i32 height i32 factor f32] Image "GenImageWhiteNoise") +(declare-c gen-image-perlin-noise [width i32 height i32 offset-x i32 offset-y i32 scale f32] Image "GenImagePerlinNoise") +(declare-c gen-image-cellular [width i32 height i32 tile-size i32] Image "GenImageCellular") +(declare-c gen-image-text [width i32 height i32 text string] Image "GenImageText") +(declare-c image-copy [image Image] Image "ImageCopy") +(declare-c image-from-image [image Image rec Rectangle] Image "ImageFromImage") +(declare-c image-from-channel [image Image selected-channel i32] Image "ImageFromChannel") +(declare-c image-text [text string font-size i32 color Color] Image "ImageText") +(declare-c image-text-ex [font Font text string font-size f32 spacing f32 tint Color] Image "ImageTextEx") +(declare-c image-format [image (Ptr Image) new-format i32] "ImageFormat") +(declare-c image-to-pot [image (Ptr Image) fill Color] "ImageToPOT") +(declare-c image-alpha-crop [image (Ptr Image) threshold f32] "ImageAlphaCrop") +(declare-c image-alpha-clear [image (Ptr Image) color Color threshold f32] "ImageAlphaClear") +(declare-c image-alpha-mask [image (Ptr Image) alpha-mask Image] "ImageAlphaMask") +(declare-c image-alpha-premultiply [image (Ptr Image)] "ImageAlphaPremultiply") +(declare-c image-blur-gaussian [image (Ptr Image) blur-size i32] "ImageBlurGaussian") +(declare-c image-kernel-convolution [image (Ptr Image) kernel (Ptr f32) kernel-size i32] "ImageKernelConvolution") +(declare-c image-resize-canvas [image (Ptr Image) new-width i32 new-height i32 offset-x i32 offset-y i32 fill Color] "ImageResizeCanvas") +(declare-c image-mipmaps [image (Ptr Image)] "ImageMipmaps") +(declare-c image-dither [image (Ptr Image) r-bpp i32 g-bpp i32 b-bpp i32 a-bpp i32] "ImageDither") +(declare-c image-rotate [image (Ptr Image) degrees i32] "ImageRotate") +(declare-c image-rotate-cw [image (Ptr Image)] "ImageRotateCW") +(declare-c image-rotate-ccw [image (Ptr Image)] "ImageRotateCCW") +(declare-c image-color-tint [image (Ptr Image) color Color] "ImageColorTint") +(declare-c image-color-invert [image (Ptr Image)] "ImageColorInvert") +(declare-c image-color-grayscale [image (Ptr Image)] "ImageColorGrayscale") +(declare-c image-color-contrast [image (Ptr Image) contrast f32] "ImageColorContrast") +(declare-c image-color-brightness [image (Ptr Image) brightness i32] "ImageColorBrightness") +(declare-c image-color-replace [image (Ptr Image) color Color replace Color] "ImageColorReplace") +(declare-c load-image-colors [image Image] (Ptr Color) "LoadImageColors") +(declare-c load-image-palette [image Image max-palette-size i32 color-count (Ptr i32)] (Ptr Color) "LoadImagePalette") +(declare-c unload-image-colors [colors (Ptr Color)] "UnloadImageColors") +(declare-c unload-image-palette [colors (Ptr Color)] "UnloadImagePalette") +(declare-c get-image-alpha-border [image Image threshold f32] Rectangle "GetImageAlphaBorder") +(declare-c image-clear-background [dst (Ptr Image) color Color] "ImageClearBackground") +(declare-c image-draw-pixel-v [dst (Ptr Image) position Vector2 color Color] "ImageDrawPixelV") +(declare-c image-draw-line [dst (Ptr Image) start-pos-x i32 start-pos-y i32 end-pos-x i32 end-pos-y i32 color Color] "ImageDrawLine") +(declare-c image-draw-line-v [dst (Ptr Image) start Vector2 end Vector2 color Color] "ImageDrawLineV") +(declare-c image-draw-line-ex [dst (Ptr Image) start Vector2 end Vector2 thick i32 color Color] "ImageDrawLineEx") +(declare-c image-draw-circle [dst (Ptr Image) center-x i32 center-y i32 radius i32 color Color] "ImageDrawCircle") +(declare-c image-draw-circle-v [dst (Ptr Image) center Vector2 radius i32 color Color] "ImageDrawCircleV") +(declare-c image-draw-circle-lines [dst (Ptr Image) center-x i32 center-y i32 radius i32 color Color] "ImageDrawCircleLines") +(declare-c image-draw-circle-lines-v [dst (Ptr Image) center Vector2 radius i32 color Color] "ImageDrawCircleLinesV") +(declare-c image-draw-rectangle [dst (Ptr Image) pos-x i32 pos-y i32 width i32 height i32 color Color] "ImageDrawRectangle") +(declare-c image-draw-rectangle-v [dst (Ptr Image) position Vector2 size Vector2 color Color] "ImageDrawRectangleV") +(declare-c image-draw-rectangle-rec [dst (Ptr Image) rec Rectangle color Color] "ImageDrawRectangleRec") +(declare-c image-draw-rectangle-lines [dst (Ptr Image) rec Rectangle thick i32 color Color] "ImageDrawRectangleLines") +(declare-c image-draw-triangle [dst (Ptr Image) v-1 Vector2 v-2 Vector2 v-3 Vector2 color Color] "ImageDrawTriangle") +(declare-c image-draw-triangle-ex [dst (Ptr Image) v-1 Vector2 v-2 Vector2 v-3 Vector2 c-1 Color c-2 Color c-3 Color] "ImageDrawTriangleEx") +(declare-c image-draw-triangle-lines [dst (Ptr Image) v-1 Vector2 v-2 Vector2 v-3 Vector2 color Color] "ImageDrawTriangleLines") +(declare-c image-draw-triangle-fan [dst (Ptr Image) points (Ptr Vector2) point-count i32 color Color] "ImageDrawTriangleFan") +(declare-c image-draw-triangle-strip [dst (Ptr Image) points (Ptr Vector2) point-count i32 color Color] "ImageDrawTriangleStrip") +(declare-c image-draw [dst (Ptr Image) src Image src-rec Rectangle dst-rec Rectangle tint Color] "ImageDraw") +(declare-c image-draw-text [dst (Ptr Image) text string pos-x i32 pos-y i32 font-size i32 color Color] "ImageDrawText") +(declare-c image-draw-text-ex [dst (Ptr Image) font Font text string position Vector2 font-size f32 spacing f32 tint Color] "ImageDrawTextEx") +(declare-c load-texture-cubemap [image Image layout i32] Texture2D "LoadTextureCubemap") +(declare-c update-texture [texture Texture2D pixels (Ptr u8)] "UpdateTexture") +(declare-c update-texture-rec [texture Texture2D rec Rectangle pixels (Ptr u8)] "UpdateTextureRec") +(declare-c gen-texture-mipmaps [texture (Ptr Texture2D)] "GenTextureMipmaps") +(declare-c set-texture-filter [texture Texture2D filter i32] "SetTextureFilter") +(declare-c set-texture-wrap [texture Texture2D wrap i32] "SetTextureWrap") +(declare-c draw-texture-pro [texture Texture2D source Rectangle dest Rectangle origin Vector2 rotation f32 tint Color] "DrawTexturePro") +(declare-c color-is-equal [col-1 Color col-2 Color] bool "ColorIsEqual") +(declare-c color-to-int [color Color] i32 "ColorToInt") +(declare-c color-from-hsv [hue f32 saturation f32 value f32] Color "ColorFromHSV") +(declare-c color-tint [color Color tint Color] Color "ColorTint") +(declare-c color-brightness [color Color factor f32] Color "ColorBrightness") +(declare-c color-contrast [color Color contrast f32] Color "ColorContrast") +(declare-c color-alpha [color Color alpha f32] Color "ColorAlpha") +(declare-c color-alpha-blend [dst Color src Color tint Color] Color "ColorAlphaBlend") +(declare-c color-lerp [color-1 Color color-2 Color factor f32] Color "ColorLerp") +(declare-c get-pixel-color [src-ptr (Ptr u8) format i32] Color "GetPixelColor") +(declare-c set-pixel-color [dst-ptr (Ptr u8) color Color format i32] "SetPixelColor") +(declare-c get-pixel-data-size [width i32 height i32 format i32] i32 "GetPixelDataSize") +(declare-c load-font-from-image [image Image key Color first-char i32] Font "LoadFontFromImage") +(declare-c load-font-from-memory [file-type string file-data (Ptr u8) data-size i32 font-size i32 codepoints (Ptr i32) codepoint-count i32] Font "LoadFontFromMemory") +(declare-c load-font-data [file-data (Ptr u8) data-size i32 font-size i32 codepoints (Ptr i32) codepoint-count i32 type i32] (Ptr GlyphInfo) "LoadFontData") +(declare-c gen-image-font-atlas [glyphs (Ptr GlyphInfo) glyph-recs (Ptr (Ptr Rectangle)) glyph-count i32 font-size i32 padding i32 pack-method i32] Image "GenImageFontAtlas") +(declare-c unload-font-data [glyphs (Ptr GlyphInfo) glyph-count i32] "UnloadFontData") +(declare-c export-font-as-code [font Font file-name string] bool "ExportFontAsCode") +(declare-c draw-text-pro [font Font text string position Vector2 origin Vector2 rotation f32 font-size f32 spacing f32 tint Color] "DrawTextPro") +(declare-c draw-text-codepoints [font Font codepoints (Ptr i32) codepoint-count i32 position Vector2 font-size f32 spacing f32 tint Color] "DrawTextCodepoints") +(declare-c set-text-line-spacing [spacing i32] "SetTextLineSpacing") +(declare-c load-codepoints [text string count (Ptr i32)] (Ptr i32) "LoadCodepoints") +(declare-c unload-codepoints [codepoints (Ptr i32)] "UnloadCodepoints") +(declare-c get-codepoint-count [text string] i32 "GetCodepointCount") +(declare-c get-codepoint [text string codepoint-size (Ptr i32)] i32 "GetCodepoint") +(declare-c get-codepoint-next [text string codepoint-size (Ptr i32)] i32 "GetCodepointNext") +(declare-c get-codepoint-previous [text string codepoint-size (Ptr i32)] i32 "GetCodepointPrevious") +(declare-c text-is-equal [text-1 string text-2 string] bool "TextIsEqual") +(declare-c text-length [text string] u32 "TextLength") +(declare-c text-split [text string delimiter i8 count (Ptr i32)] (Ptr (Ptr i8)) "TextSplit") +(declare-c text-find-index [text string find string] i32 "TextFindIndex") +(declare-c text-to-integer [text string] i32 "TextToInteger") +(declare-c text-to-float [text string] f32 "TextToFloat") +(declare-c draw-grid [slices i32 spacing f32] "DrawGrid") +(declare-c load-wave-from-memory [file-type string file-data (Ptr u8) data-size i32] Wave "LoadWaveFromMemory") +(declare-c update-sound [sound Sound data (Ptr u8) sample-count i32] "UpdateSound") +(declare-c export-wave-as-code [wave Wave file-name string] bool "ExportWaveAsCode") +(declare-c load-music-stream-from-memory [file-type string data (Ptr u8) data-size i32] Music "LoadMusicStreamFromMemory") +(declare-c load-audio-stream [sample-rate u32 sample-size u32 channels u32] AudioStream "LoadAudioStream") +(declare-c audio-stream-valid? [stream AudioStream] bool "IsAudioStreamValid") +(declare-c unload-audio-stream [stream AudioStream] "UnloadAudioStream") +(declare-c update-audio-stream [stream AudioStream data (Ptr u8) frame-count i32] "UpdateAudioStream") +(declare-c audio-stream-processed? [stream AudioStream] bool "IsAudioStreamProcessed") +(declare-c play-audio-stream [stream AudioStream] "PlayAudioStream") +(declare-c pause-audio-stream [stream AudioStream] "PauseAudioStream") +(declare-c resume-audio-stream [stream AudioStream] "ResumeAudioStream") +(declare-c audio-stream-playing? [stream AudioStream] bool "IsAudioStreamPlaying") +(declare-c stop-audio-stream [stream AudioStream] "StopAudioStream") +(declare-c set-audio-stream-volume [stream AudioStream volume f32] "SetAudioStreamVolume") +(declare-c set-audio-stream-pitch [stream AudioStream pitch f32] "SetAudioStreamPitch") +(declare-c set-audio-stream-pan [stream AudioStream pan f32] "SetAudioStreamPan") +(declare-c set-audio-stream-buffer-size-default [size i32] "SetAudioStreamBufferSizeDefault") diff --git a/vendor/raylib/headers b/vendor/raylib/headers index 4f001e7..3cbc5c1 100644 --- a/vendor/raylib/headers +++ b/vendor/raylib/headers @@ -4,29 +4,49 @@ # means "if it is there" — an optional line with nothing behind it is simply # not read. # -# Why this exists. The declare-c lines in raylib.flan were transcribed by -# hand from raylib's documentation, and until now nothing could check that -# any of them matched the real function — BUILT.md records that as trusted -# rather than guaranteed. Point this at raylib's own header and the compiler -# reads the signatures instead: every hand-written line is compared against -# the library's, every defstruct against the header's record, and any raylib -# function the package has not bound becomes available under its own name. +# What this is for, now that the bindings are committed. generated.flan holds +# every declaration the importer produced, in the repository, so a build needs +# libraylib linkable and no header at all. This line is read by two things: # -# Why it is optional. A build needs libraylib linkable and *not* raylib-devel -# installed, which is a property worth keeping; requiring a header would take -# it from everyone to give the check to whoever has one. So the default build -# is unchanged and this is opt-in, the same shape as ${FLAN_RAYLIB_WEB} in -# `link`. +# 1. `flan generate-c vendor/raylib`, which is the only way generated.flan +# is written. It reads the header named here, compares the package +# against it, and refuses to write when they disagree — so it is not +# possible to regenerate the bindings without comparing them to the +# library they claim to bind. +# +# 2. an ordinary build, when the variable happens to be set. Every C symbol +# is bound already — by hand in raylib.flan or by generation in +# generated.flan — so the importer generates nothing and the header read +# is purely the check. That check is now over all 425 declarations rather +# than the 172 hand-written ones, because the generated file is a package +# file like any other and is checked like one. +# +# Why it is still optional. A build needs libraylib linkable and *not* +# raylib-devel installed, which is a property worth keeping; requiring a header +# would take it from everyone to give the check to whoever has one. Before the +# bindings were committed this marker also decided how many bindings a build +# got, which was the real cost of it being opt-in; it no longer decides that, +# and all it now withholds is a check that regeneration has already run once. +# Same shape as ${FLAN_RAYLIB_WEB} in `link`, and for the same reason. # # The version must match the shared library `link` names — 5.5, libraylib.so.550. # Reading one version's header while linking another's library is exactly the -# silent disagreement this exists to prevent, and `flan import-c` will say so: -# against a 5.1-dev header it reports ten differences that are all real. +# silent disagreement this exists to prevent, and it is caught rather than +# described: against a 5.1-dev header, regeneration reports ten differences +# that are all real and writes nothing. # # export FLAN_RAYLIB_H=/path/to/raylib-5.5/src/raylib.h # -# To see what it would do without building anything: +# vendor/raylib/build-web.sh already clones that exact tag to build the browser +# archive, so a tree that has built for web has the matching header at +# vendor/raylib/web/raylib-5.5/src/raylib.h. +# +# To see what regeneration would produce without writing anything: # # flan import-c $FLAN_RAYLIB_H vendor/raylib/raylib.flan # +# What shapes the generated half — which functions are skipped, and what they +# are called — is `bindings` beside this file. See its comments for why a +# committed generated file needs a config at all. +# ?${FLAN_RAYLIB_H}