Bind the parts of raylib a game needs and this one refused
Audio, render textures, fonts, gamepads, touch and gestures were all absent, and a game cannot ship without the first of them. Fonts were refused by name last time because a Font drags in two more aggregates and two owned arrays with nothing headless to check them against; the generator takes all of it unchanged now, and the check turned out to exist — raylib measures text with pure CPU arithmetic over every field. SetGamepadVibration stays unbound for two reasons at once: its arity differs between the 5.1 and 6.1 headers with no 5.5 header to settle it, and the symbol in libraylib.so.550 disassembles to a TraceLog stub that touches no motor.
This commit is contained in:
parent
5f0bde8149
commit
826c62a1e9
2
.gitignore
vendored
2
.gitignore
vendored
@ -43,3 +43,5 @@ old-ocaml/
|
|||||||
/sand
|
/sand
|
||||||
/conditions-play
|
/conditions-play
|
||||||
.claude/
|
.claude/
|
||||||
|
probe
|
||||||
|
probe.c
|
||||||
|
|||||||
419
vendor/raylib/raylib.flan
vendored
419
vendor/raylib/raylib.flan
vendored
@ -459,12 +459,12 @@
|
|||||||
;; libraylib.so.550 and is why it is NOT in the acceptance table despite
|
;; libraylib.so.550 and is why it is NOT in the acceptance table despite
|
||||||
;; looking like exactly the kind of call that could be.
|
;; looking like exactly the kind of call that could be.
|
||||||
;;
|
;;
|
||||||
;; Font loading is not bound, deliberately. A Font is baseSize, glyphCount and
|
;; Fonts ARE bound now — see the section at the end of this file. The reason
|
||||||
;; glyphPadding beside a Texture2D, a Rectangle* and a GlyphInfo* — and a
|
;; they were not is worth keeping: a Font is three ints beside a Texture2D, a
|
||||||
;; GlyphInfo embeds an Image. Binding it means binding two more aggregates and
|
;; Rectangle* and a GlyphInfo*, and a GlyphInfo embeds an Image, and that was
|
||||||
;; two owned arrays for something with no headless test at the end of it, so
|
;; "two more aggregates and two owned arrays for something with no headless
|
||||||
;; load-font, load-font-ex, unload-font, get-font-default, draw-text-ex and
|
;; test at the end of it". The generator takes all of that now, and the
|
||||||
;; measure-text-ex are all absent rather than half-done.
|
;; headless test turned out to exist after all.
|
||||||
|
|
||||||
(declare-c draw-text
|
(declare-c draw-text
|
||||||
[text string x i32 y i32 font-size i32 color Color]
|
[text string x i32 y i32 font-size i32 color Color]
|
||||||
@ -483,3 +483,410 @@
|
|||||||
(declare-c get-time [] f64 "GetTime")
|
(declare-c get-time [] f64 "GetTime")
|
||||||
(declare-c get-screen-width [] i32 "GetScreenWidth")
|
(declare-c get-screen-width [] i32 "GetScreenWidth")
|
||||||
(declare-c get-screen-height [] i32 "GetScreenHeight")
|
(declare-c get-screen-height [] i32 "GetScreenHeight")
|
||||||
|
|
||||||
|
;; ── Gamepads ────────────────────────────────────────────────────────
|
||||||
|
;;
|
||||||
|
;; Nothing here can be asserted headlessly and nothing here can be asserted
|
||||||
|
;; *at all* without a pad plugged in: with no gamepad, gamepad-available? is
|
||||||
|
;; false, every button predicate is false and every axis reads 0.0, which is
|
||||||
|
;; also exactly what a wrapper with its two int arguments exchanged would
|
||||||
|
;; report. So these are bound, wired into sand.flan's HUD, and honestly
|
||||||
|
;; described as untested — the only check they get is that a pad moves the
|
||||||
|
;; read-out.
|
||||||
|
;;
|
||||||
|
;; `pad` is an index from 0, not an enum: raylib's own parameter is an int and
|
||||||
|
;; how many are attached is a run-time question.
|
||||||
|
|
||||||
|
(defenum GamepadButton
|
||||||
|
[unknown 0
|
||||||
|
left-face-up 1 left-face-right 2 left-face-down 3 left-face-left 4
|
||||||
|
right-face-up 5 right-face-right 6 right-face-down 7 right-face-left 8
|
||||||
|
left-trigger-1 9 left-trigger-2 10
|
||||||
|
right-trigger-1 11 right-trigger-2 12
|
||||||
|
middle-left 13 middle 14 middle-right 15
|
||||||
|
left-thumb 16 right-thumb 17])
|
||||||
|
|
||||||
|
;; The triggers read -1 at rest and 1 fully pressed, unlike the sticks, which
|
||||||
|
;; are centred at 0. raylib does not normalise that and neither does this.
|
||||||
|
(defenum GamepadAxis
|
||||||
|
[left-x 0 left-y 1 right-x 2 right-y 3
|
||||||
|
left-trigger 4 right-trigger 5])
|
||||||
|
|
||||||
|
(declare-c gamepad-available? [pad i32] bool "IsGamepadAvailable")
|
||||||
|
|
||||||
|
(declare-c gamepad-button-pressed?
|
||||||
|
[pad i32 button GamepadButton] bool
|
||||||
|
"IsGamepadButtonPressed")
|
||||||
|
(declare-c gamepad-button-down?
|
||||||
|
[pad i32 button GamepadButton] bool
|
||||||
|
"IsGamepadButtonDown")
|
||||||
|
(declare-c gamepad-button-released?
|
||||||
|
[pad i32 button GamepadButton] bool
|
||||||
|
"IsGamepadButtonReleased")
|
||||||
|
(declare-c gamepad-button-up?
|
||||||
|
[pad i32 button GamepadButton] bool
|
||||||
|
"IsGamepadButtonUp")
|
||||||
|
|
||||||
|
;; -1 when nothing is pressed, so the answer is not a GamepadButton: raylib
|
||||||
|
;; returns an int outside the enum and the checker would have to be lied to.
|
||||||
|
(declare-c get-gamepad-button-pressed [] i32 "GetGamepadButtonPressed")
|
||||||
|
|
||||||
|
(declare-c get-gamepad-axis-count [pad i32] i32 "GetGamepadAxisCount")
|
||||||
|
|
||||||
|
(declare-c get-gamepad-axis-movement
|
||||||
|
[pad i32 axis GamepadAxis] f32
|
||||||
|
"GetGamepadAxisMovement")
|
||||||
|
|
||||||
|
;; SetGamepadVibration is NOT bound, and the reason is not the usual one. Two
|
||||||
|
;; things are wrong with it at once. Its arity changed — 5.1-dev takes three
|
||||||
|
;; floats and 6.1-dev takes four, there is no 5.5 header here to settle which,
|
||||||
|
;; and the generated prototype is what fixes the call, so a guess is a
|
||||||
|
;; corrupted stack frame rather than a link error. And it would not matter if
|
||||||
|
;; it were guessed right: the symbol in libraylib.so.550 disassembles to a
|
||||||
|
;; single TraceLog call and a jump — it is a stub that reports "not
|
||||||
|
;; implemented" and touches no motor. Binding it would be binding a warning.
|
||||||
|
|
||||||
|
;; ── Touch and gestures ──────────────────────────────────────────────
|
||||||
|
;;
|
||||||
|
;; Touch is a superset of the mouse on a desktop: with no touchscreen,
|
||||||
|
;; get-touch-point-count is 0 but get-touch-position 0 still tracks the mouse,
|
||||||
|
;; which is what makes it visible in sand.flan at all.
|
||||||
|
;;
|
||||||
|
;; The gesture system is fed by raylib's own event polling, so a gesture is
|
||||||
|
;; only ever detected inside a window's frame loop — nothing here is headless
|
||||||
|
;; material either.
|
||||||
|
|
||||||
|
;; A bitfield, not an ordinary enum: set-gestures-enabled takes the OR of
|
||||||
|
;; several and gesture-detected? tests against one. That is why the enabling
|
||||||
|
;; call below takes a u32 and not a Gesture — a keyword can only ever name one
|
||||||
|
;; member, and `all` is spelled out here so the common case still reads.
|
||||||
|
(defenum Gesture
|
||||||
|
[none 0 tap 1 double-tap 2 hold 4 drag 8
|
||||||
|
swipe-right 16 swipe-left 32 swipe-up 64 swipe-down 128
|
||||||
|
pinch-in 256 pinch-out 512])
|
||||||
|
|
||||||
|
(defconst gesture-all u32 1023)
|
||||||
|
|
||||||
|
(declare-c get-touch-position [index i32] Vector2 "GetTouchPosition")
|
||||||
|
(declare-c get-touch-x [] i32 "GetTouchX")
|
||||||
|
(declare-c get-touch-y [] i32 "GetTouchY")
|
||||||
|
(declare-c get-touch-point-count [] i32 "GetTouchPointCount")
|
||||||
|
(declare-c get-touch-point-id [index i32] i32 "GetTouchPointId")
|
||||||
|
|
||||||
|
(declare-c set-gestures-enabled [flags u32] "SetGesturesEnabled")
|
||||||
|
(declare-c gesture-detected? [gesture Gesture] bool "IsGestureDetected")
|
||||||
|
(declare-c get-gesture-detected [] Gesture "GetGestureDetected")
|
||||||
|
|
||||||
|
;; Degrees, and only meaningful while a drag is in progress.
|
||||||
|
(declare-c get-gesture-drag-vector [] Vector2 "GetGestureDragVector")
|
||||||
|
(declare-c get-gesture-drag-angle [] f32 "GetGestureDragAngle")
|
||||||
|
(declare-c get-gesture-pinch-vector [] Vector2 "GetGesturePinchVector")
|
||||||
|
(declare-c get-gesture-pinch-angle [] f32 "GetGesturePinchAngle")
|
||||||
|
(declare-c get-gesture-hold-duration [] f32 "GetGestureHoldDuration")
|
||||||
|
|
||||||
|
;; ── Render textures ─────────────────────────────────────────────────
|
||||||
|
;;
|
||||||
|
;; A framebuffer with two textures hanging off it: draw into it between
|
||||||
|
;; begin-texture-mode and end-texture-mode, then draw *it* like any other
|
||||||
|
;; texture. That is how a post-process pass and a pixel-perfect integer
|
||||||
|
;; upscale are both done.
|
||||||
|
;;
|
||||||
|
;; None of it is assertable here — LoadRenderTexture makes a GL framebuffer
|
||||||
|
;; object, so with no context it answers an id of 0 and every draw into it is
|
||||||
|
;; a no-op. `depth` is a renderbuffer rather than a real texture in raylib's
|
||||||
|
;; default configuration, so its id is the only field of it worth reading.
|
||||||
|
;;
|
||||||
|
;; The one thing the layout gets for free: RenderTexture2D is a u32 and two
|
||||||
|
;; Texture2Ds, and Texture2D is already pinned as far as anything headless can
|
||||||
|
;; pin it, so the only new claim here is the order of the three members.
|
||||||
|
;; sand.flan draws through one, which is where a wrong order shows up.
|
||||||
|
|
||||||
|
(defstruct RenderTexture2D [id u32 texture Texture2D depth Texture2D])
|
||||||
|
|
||||||
|
(declare-c load-render-texture
|
||||||
|
[width i32 height i32] RenderTexture2D
|
||||||
|
"LoadRenderTexture")
|
||||||
|
|
||||||
|
;; raylib 5.5 spells this IsRenderTextureValid; there is no IsRenderTextureReady
|
||||||
|
;; in this version, the same rename that took IsTextureReady and IsImageReady.
|
||||||
|
(declare-c render-texture-valid?
|
||||||
|
[target RenderTexture2D] bool
|
||||||
|
"IsRenderTextureValid")
|
||||||
|
|
||||||
|
(declare-c unload-render-texture
|
||||||
|
[target RenderTexture2D]
|
||||||
|
"UnloadRenderTexture")
|
||||||
|
|
||||||
|
;; Everything drawn between these two lands in the target instead of the
|
||||||
|
;; screen, and the target's texture comes out of the GPU upside down — raylib
|
||||||
|
;; renders it bottom-up — so drawing it back with a NEGATIVE source height is
|
||||||
|
;; not a flourish, it is the correction. sand.flan does exactly that.
|
||||||
|
(declare-c begin-texture-mode [target RenderTexture2D] "BeginTextureMode")
|
||||||
|
(declare-c end-texture-mode [] "EndTextureMode")
|
||||||
|
|
||||||
|
;; ── Audio ───────────────────────────────────────────────────────────
|
||||||
|
;;
|
||||||
|
;; The device first, and the split that matters for testing runs right
|
||||||
|
;; through this section: a **Wave** is samples in RAM and needs no device at
|
||||||
|
;; all, while a **Sound** is a buffer the mixer owns and a **Music** is a
|
||||||
|
;; decoder feeding one, and both of those are nothing without
|
||||||
|
;; init-audio-device having succeeded.
|
||||||
|
;;
|
||||||
|
;; That makes Wave the audio equivalent of the Image family — raylib
|
||||||
|
;; *computes* with it, headlessly — and it is the only part of this section
|
||||||
|
;; the acceptance table asserts. Everything from Sound down is exercised by
|
||||||
|
;; running sand.flan with a working sound server, and a machine without one
|
||||||
|
;; gets silence rather than a crash: init-audio-device logs a warning, every
|
||||||
|
;; load answers a zeroed struct and every play is a no-op.
|
||||||
|
|
||||||
|
(declare-c init-audio-device [] "InitAudioDevice")
|
||||||
|
(declare-c close-audio-device [] "CloseAudioDevice")
|
||||||
|
(declare-c audio-device-ready? [] bool "IsAudioDeviceReady")
|
||||||
|
|
||||||
|
;; 0 to 1, and it is a listener gain applied after every per-sound volume.
|
||||||
|
(declare-c set-master-volume [volume f32] "SetMasterVolume")
|
||||||
|
(declare-c get-master-volume [] f32 "GetMasterVolume")
|
||||||
|
|
||||||
|
;; A Wave is the CPU side: `data` is frame-count × channels samples of
|
||||||
|
;; sample-size bits each, and raylib reads every one of the four integers to
|
||||||
|
;; decide what those bytes mean. `data` is (Ptr u8) rather than a typed
|
||||||
|
;; pointer because its element type is `sample-size`, which is a run-time
|
||||||
|
;; number — 8, 16 or 32 bits — and there is no Flan type that says that.
|
||||||
|
;;
|
||||||
|
;; The consequence for a caller building one by hand: the bytes are written
|
||||||
|
;; as bytes, in the host's order. That is the shape the acceptance case uses,
|
||||||
|
;; and it is deliberate — it means the case says what it means about
|
||||||
|
;; little-endian 16-bit PCM instead of hiding it behind a cast.
|
||||||
|
(defstruct Wave [frame-count u32 sample-rate u32 sample-size u32
|
||||||
|
channels u32 data (Ptr u8)])
|
||||||
|
|
||||||
|
(declare-c load-wave [path string] Wave "LoadWave")
|
||||||
|
|
||||||
|
;; raylib 5.5 spells this IsWaveValid; IsWaveReady is gone, as everywhere else.
|
||||||
|
(declare-c wave-valid? [wave Wave] bool "IsWaveValid")
|
||||||
|
|
||||||
|
(declare-c unload-wave [wave Wave] "UnloadWave")
|
||||||
|
|
||||||
|
;; The extension picks the format, and raylib writes .wav and .qoa. This is
|
||||||
|
;; external ground truth for the layout: the header it writes carries
|
||||||
|
;; sample-rate, sample-size and channels, and the payload length carries
|
||||||
|
;; frame-count, so a permuted defstruct writes a file that reads back
|
||||||
|
;; differently — the same argument the PNG round trip makes for Image.
|
||||||
|
(declare-c export-wave [wave Wave path string] bool "ExportWave")
|
||||||
|
|
||||||
|
;; Allocates a copy of the buffer; the copy is unloaded on its own.
|
||||||
|
(declare-c wave-copy [wave Wave] Wave "WaveCopy")
|
||||||
|
|
||||||
|
;; In FRAMES, not samples — raylib renamed the parameters for 5.5 without
|
||||||
|
;; changing the signature, so the name is the only thing that says which. On
|
||||||
|
;; a mono wave the two readings coincide, which is what the acceptance case
|
||||||
|
;; uses, so nothing here depends on having guessed right.
|
||||||
|
(declare-c wave-crop
|
||||||
|
[wave (Ptr Wave) init-frame i32 final-frame i32]
|
||||||
|
"WaveCrop")
|
||||||
|
|
||||||
|
;; Resamples in place. This is the strongest headless shape available in this
|
||||||
|
;; section and the same one gen-image-color has: three scalars go in and four
|
||||||
|
;; fields come out, with frame-count *computed* from the sample-rate ratio, so
|
||||||
|
;; a permuted layout has nothing to cancel against.
|
||||||
|
(declare-c wave-format
|
||||||
|
[wave (Ptr Wave) sample-rate i32 sample-size i32 channels i32]
|
||||||
|
"WaveFormat")
|
||||||
|
|
||||||
|
;; Every sample as a float in [-1, 1], frame-count × channels of them,
|
||||||
|
;; whatever the wave's own sample-size. That is the one call that reads
|
||||||
|
;; *through* `data`, so it is what pins the pointer as a pointer rather than
|
||||||
|
;; as two integers that happen to sit at the end.
|
||||||
|
(declare-c load-wave-samples [wave Wave] (Ptr f32) "LoadWaveSamples")
|
||||||
|
(declare-c unload-wave-samples [samples (Ptr f32)] "UnloadWaveSamples")
|
||||||
|
|
||||||
|
;; A Sound is an AudioStream plus a frame count. The two leading pointers are
|
||||||
|
;; miniaudio's and Flan never reads through them — they are (Ptr u8) so the
|
||||||
|
;; struct is the right size and the three integers land at the right offsets,
|
||||||
|
;; exactly as Image's `data` is.
|
||||||
|
(defstruct AudioStream [buffer (Ptr u8) processor (Ptr u8)
|
||||||
|
sample-rate u32 sample-size u32 channels u32])
|
||||||
|
|
||||||
|
(defstruct Sound [stream AudioStream frame-count u32])
|
||||||
|
|
||||||
|
(declare-c load-sound [path string] Sound "LoadSound")
|
||||||
|
|
||||||
|
;; Note what this does to the frame count: the mixer resamples to the device's
|
||||||
|
;; own rate, so a sound made from an 8 kHz wave on a 48 kHz device reports six
|
||||||
|
;; times as many frames. Nothing should read `frame-count` expecting the
|
||||||
|
;; wave's.
|
||||||
|
(declare-c load-sound-from-wave [wave Wave] Sound "LoadSoundFromWave")
|
||||||
|
|
||||||
|
(declare-c sound-valid? [sound Sound] bool "IsSoundValid")
|
||||||
|
(declare-c unload-sound [sound Sound] "UnloadSound")
|
||||||
|
|
||||||
|
(declare-c play-sound [sound Sound] "PlaySound")
|
||||||
|
(declare-c stop-sound [sound Sound] "StopSound")
|
||||||
|
(declare-c pause-sound [sound Sound] "PauseSound")
|
||||||
|
(declare-c resume-sound [sound Sound] "ResumeSound")
|
||||||
|
(declare-c sound-playing? [sound Sound] bool "IsSoundPlaying")
|
||||||
|
|
||||||
|
;; Volume is a gain from 0, pitch is a rate multiplier where 1 is unchanged,
|
||||||
|
;; and pan is 0 hard left to 1 hard right with 0.5 centred — raylib's own
|
||||||
|
;; convention, and the one place in this file where 0 is not the neutral
|
||||||
|
;; value.
|
||||||
|
(declare-c set-sound-volume [sound Sound volume f32] "SetSoundVolume")
|
||||||
|
(declare-c set-sound-pitch [sound Sound pitch f32] "SetSoundPitch")
|
||||||
|
(declare-c set-sound-pan [sound Sound pan f32] "SetSoundPan")
|
||||||
|
|
||||||
|
;; A second voice over the same samples, so one sound can overlap itself. It
|
||||||
|
;; does NOT own the data, so unloading an alias must not unload the original —
|
||||||
|
;; which is why raylib has a separate call for it and why this one is bound.
|
||||||
|
(declare-c load-sound-alias [source Sound] Sound "LoadSoundAlias")
|
||||||
|
(declare-c unload-sound-alias [alias Sound] "UnloadSoundAlias")
|
||||||
|
|
||||||
|
;; Music is streamed rather than resident, which is the whole difference: the
|
||||||
|
;; buffer is refilled from the decoder and update-music-stream is what does
|
||||||
|
;; the refilling. Miss it for a frame and the music stops.
|
||||||
|
(defstruct Music [stream AudioStream frame-count u32 looping bool
|
||||||
|
ctx-type i32 ctx-data (Ptr u8)])
|
||||||
|
|
||||||
|
(declare-c load-music-stream [path string] Music "LoadMusicStream")
|
||||||
|
(declare-c music-valid? [music Music] bool "IsMusicValid")
|
||||||
|
(declare-c unload-music-stream [music Music] "UnloadMusicStream")
|
||||||
|
|
||||||
|
(declare-c play-music-stream [music Music] "PlayMusicStream")
|
||||||
|
|
||||||
|
;; Called once per frame, every frame, for as long as the music is meant to
|
||||||
|
;; play. This is the one binding in the section whose absence is silent.
|
||||||
|
(declare-c update-music-stream [music Music] "UpdateMusicStream")
|
||||||
|
|
||||||
|
(declare-c stop-music-stream [music Music] "StopMusicStream")
|
||||||
|
(declare-c pause-music-stream [music Music] "PauseMusicStream")
|
||||||
|
(declare-c resume-music-stream [music Music] "ResumeMusicStream")
|
||||||
|
(declare-c music-stream-playing? [music Music] bool "IsMusicStreamPlaying")
|
||||||
|
|
||||||
|
(declare-c set-music-volume [music Music volume f32] "SetMusicVolume")
|
||||||
|
(declare-c set-music-pitch [music Music pitch f32] "SetMusicPitch")
|
||||||
|
(declare-c set-music-pan [music Music pan f32] "SetMusicPan")
|
||||||
|
|
||||||
|
;; Seconds, both of them.
|
||||||
|
(declare-c seek-music-stream [music Music position f32] "SeekMusicStream")
|
||||||
|
(declare-c get-music-time-length [music Music] f32 "GetMusicTimeLength")
|
||||||
|
(declare-c get-music-time-played [music Music] f32 "GetMusicTimePlayed")
|
||||||
|
|
||||||
|
;; AudioStream itself — the raw callback-fed stream — is NOT bound. Its point
|
||||||
|
;; is set-audio-stream-callback, which takes a C function pointer, and a
|
||||||
|
;; callback is refused by the shim generator by name: `%s is a function type,
|
||||||
|
;; and a C callback is not implemented`. Binding the rest of the family
|
||||||
|
;; without it would be binding a stream that can only ever be fed by
|
||||||
|
;; update-audio-stream from the main thread, which is a worse Sound.
|
||||||
|
|
||||||
|
;; ── Fonts ───────────────────────────────────────────────────────────
|
||||||
|
;;
|
||||||
|
;; A previous pass refused this whole family by name, and the reason was that
|
||||||
|
;; a Font drags in two more aggregates and two owned arrays and there was
|
||||||
|
;; nothing headless to check them against. Both halves of that have changed.
|
||||||
|
;;
|
||||||
|
;; The generator takes it: a struct held by value is emitted after everything
|
||||||
|
;; it contains, a struct held by POINTER is forward-declared, and both the
|
||||||
|
;; Flan struct and the C typedef come from the same `defstruct`. Font holds a
|
||||||
|
;; Texture2D by value and points at Rectangle and GlyphInfo; GlyphInfo holds
|
||||||
|
;; an Image by value. Nothing here needed a generator change.
|
||||||
|
;;
|
||||||
|
;; And the test exists. raylib's text measuring is pure CPU arithmetic over
|
||||||
|
;; every field of a Font — it walks the glyph array looking for a codepoint,
|
||||||
|
;; reads the advance out of the glyph or the width out of the atlas rectangle,
|
||||||
|
;; and scales by the base size. The catch was that the calls that MAKE a font
|
||||||
|
;; all need something a headless run does not have: get-font-default needs
|
||||||
|
;; init-window, load-font-ex needs a TTF on disk. So the acceptance case does
|
||||||
|
;; not make one — it *builds* one, field by field, out of Flan arrays, and
|
||||||
|
;; hands it to raylib to compute with. Scalars in, numbers out, with no input
|
||||||
|
;; struct raylib produced for a permutation to cancel against.
|
||||||
|
;;
|
||||||
|
;; One trap found while doing that, and it is in raylib rather than here:
|
||||||
|
;; MeasureTextEx returns (0,0) immediately when `texture.id` is 0. A
|
||||||
|
;; hand-built font therefore has to claim a nonzero texture id even though
|
||||||
|
;; there is no texture — which is also what makes the case pin where the
|
||||||
|
;; Texture2D sits inside the Font.
|
||||||
|
|
||||||
|
;; `image` is the glyph's own pixels, and raylib owns them; it is here so the
|
||||||
|
;; four ints in front of it are at the right offsets and so a GlyphInfo is 40
|
||||||
|
;; bytes rather than 16. offset-x and offset-y shift the glyph when drawn;
|
||||||
|
;; advance-x is how far the pen moves after it, and when it is 0 raylib falls
|
||||||
|
;; back to the atlas rectangle's width plus offset-x.
|
||||||
|
(defstruct GlyphInfo [value i32 offset-x i32 offset-y i32 advance-x i32
|
||||||
|
image Image])
|
||||||
|
|
||||||
|
;; `recs` and `glyphs` are parallel arrays of glyph-count entries each: recs
|
||||||
|
;; says where the glyph is in the atlas texture, glyphs says what it is. A
|
||||||
|
;; codepoint raylib cannot find falls back to index 0 rather than reading out
|
||||||
|
;; of bounds.
|
||||||
|
(defstruct Font [base-size i32 glyph-count i32 glyph-padding i32
|
||||||
|
texture Texture2D recs (Ptr Rectangle)
|
||||||
|
glyphs (Ptr GlyphInfo)])
|
||||||
|
|
||||||
|
;; Needs a window: the default font is loaded as part of init-window and
|
||||||
|
;; LoadFontDefault is not exported, which is the same fact that makes
|
||||||
|
;; measure-text answer 0 headless.
|
||||||
|
(declare-c get-font-default [] Font "GetFontDefault")
|
||||||
|
|
||||||
|
(declare-c load-font [path string] Font "LoadFont")
|
||||||
|
|
||||||
|
;; The codepoint set is a C array plus an int count, so — like
|
||||||
|
;; collision-point-poly? — the declaration says (Ptr i32) and the Flan wrapper
|
||||||
|
;; below takes a slice apart. A slice parameter in a declare-c is refused by
|
||||||
|
;; name, because the C count's own type is not recoverable from [T].
|
||||||
|
;;
|
||||||
|
;; raylib's own convention is that a NULL pointer with a count of 0 means the
|
||||||
|
;; default ASCII set, and the wrapper keeps it — but Flan has no null pointer
|
||||||
|
;; literal, so the null comes from the one place the language does hand out
|
||||||
|
;; zeroed bytes: a `defvar` with no initialiser is BSS (plan.org, zero
|
||||||
|
;; values), and a zeroed (Ptr i32) is exactly a null one. It is never written
|
||||||
|
;; to and never read through; raylib only ever compares it against NULL.
|
||||||
|
(defvar default-codepoints (Ptr i32))
|
||||||
|
|
||||||
|
(declare-c load-font-ex-raw
|
||||||
|
[path string font-size i32 codepoints (Ptr i32) count i32] Font
|
||||||
|
"LoadFontEx")
|
||||||
|
|
||||||
|
(defn load-font-ex [path string font-size i32 codepoints [i32]] Font
|
||||||
|
(if (= (len codepoints) 0)
|
||||||
|
(load-font-ex-raw path font-size default-codepoints 0)
|
||||||
|
(load-font-ex-raw path font-size (addr (at codepoints 0)) (len codepoints))))
|
||||||
|
|
||||||
|
;; raylib 5.5 spells this IsFontValid. It reads the texture id and both
|
||||||
|
;; arrays, so a font that loaded but could not upload its atlas — which is
|
||||||
|
;; every font loaded without a GL context — is NOT valid by this test.
|
||||||
|
(declare-c font-valid? [font Font] bool "IsFontValid")
|
||||||
|
|
||||||
|
(declare-c unload-font [font Font] "UnloadFont")
|
||||||
|
|
||||||
|
;; `spacing` is extra pixels between glyphs, added per gap and not per glyph,
|
||||||
|
;; so a one-character string is unaffected by it. raylib's own DrawTextEx adds
|
||||||
|
;; it the same way measure-text-ex counts it, which is why the two agree.
|
||||||
|
(declare-c draw-text-ex [font Font text string position Vector2
|
||||||
|
font-size f32 spacing f32 tint Color] "DrawTextEx")
|
||||||
|
|
||||||
|
;; Pure arithmetic over the font — no GL, no window — and therefore the one
|
||||||
|
;; thing in this section the acceptance table can assert. See the note above:
|
||||||
|
;; it refuses to measure anything at all when the font's texture id is 0.
|
||||||
|
(declare-c measure-text-ex
|
||||||
|
[font Font text string font-size f32 spacing f32] Vector2
|
||||||
|
"MeasureTextEx")
|
||||||
|
|
||||||
|
;; The index into `recs` and `glyphs`, by linear search over glyph-count. Also
|
||||||
|
;; pure CPU, and it is what pins glyph-count as the loop bound.
|
||||||
|
(declare-c get-glyph-index [font Font codepoint i32] i32 "GetGlyphIndex")
|
||||||
|
|
||||||
|
(declare-c get-glyph-info [font Font codepoint i32] GlyphInfo "GetGlyphInfo")
|
||||||
|
|
||||||
|
(declare-c get-glyph-atlas-rec
|
||||||
|
[font Font codepoint i32] Rectangle
|
||||||
|
"GetGlyphAtlasRec")
|
||||||
|
|
||||||
|
(declare-c draw-text-codepoint [font Font codepoint i32 position Vector2
|
||||||
|
font-size f32 tint Color] "DrawTextCodepoint")
|
||||||
|
|
||||||
|
;; DrawTextCodepoints and LoadFontData are not bound. The first is the slice
|
||||||
|
;; problem again and adds nothing draw-text-ex does not already do from a
|
||||||
|
;; string; the second hands back a raw GlyphInfo array whose length is the
|
||||||
|
;; caller's to remember and whose lifetime is UnloadFontData's, and Flan has
|
||||||
|
;; no owning array type to give that to — a (Ptr GlyphInfo) with a separate
|
||||||
|
;; count is what the language would force, which is the C API with the safety
|
||||||
|
;; removed rather than a binding.
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user