(import rl "vendor:raylib") ;; The raylib boundary, headless. GetColor, the shapes texture and rectangle ;; intersection all need no window, so the whole crossing — a struct out of C ;; through an out-pointer, a struct into C through a pointer, a keyword ;; resolved against an enum — is exercised without a display. ;; ;; What is being checked is that a struct's FIELDS mean the same thing on both ;; sides. Note what does not check that: handing raylib a struct and reading it ;; back, because storing and returning is symmetric and a permuted layout ;; survives it unchanged. Every case below is asymmetric — raylib does ;; something to the fields that depends on which is which. (defn show-texture [t rl/Texture2D] (print-i64 (i64 (.id t))) (newline) (print-i64 (i64 (.width t))) (newline) (print-i64 (i64 (.height t))) (newline) (print-i64 (i64 (.mipmaps t))) (newline) (print-i64 (i64 (.format t))) (newline)) (defn show-rect [r rl/Rectangle] (print-f64 (f64 (.x r))) (newline) (print-f64 (f64 (.y r))) (newline) (print-f64 (f64 (.width r))) (newline) (print-f64 (f64 (.height r))) (newline)) (defn main [] i32 (rl/set-trace-log-level :warning) ;; A Color is four bytes in RGBA order, so 0x11223344 is 17 34 51 68 and not ;; the little-endian reading of the packed integer. An identity would pass a ;; weaker test than this one. (let [c (rl/get-color 0x11223344)] (print-i64 (i64 (.r c))) (newline) (print-i64 (i64 (.g c))) (newline) (print-i64 (i64 (.b c))) (newline) (print-i64 (i64 (.a c))) (newline)) ;; Rectangle, pinned completely. The intersection of (0,0,10,4) and ;; (6,1,10,10) is (6,1,4,3) — four different numbers, each derived from a ;; different pair of fields, so swapping any two fields changes the answer. (show-rect (rl/get-collision-rec (rl/Rectangle {:x 0.0 :y 0.0 :width 10.0 :height 4.0}) (rl/Rectangle {:x 6.0 :y 1.0 :width 10.0 :height 10.0}))) ;; Texture2D, as far as a machine with no GPU can go. raylib keeps the ;; shapes texture without touching GL, and substitutes a default when ;; `texture.id`, `source.width` or `source.height` is zero — that guard is ;; the only asymmetry a headless test gets. (let [rect (rl/Rectangle {:x 3.5 :y 7.25 :width 11.5 :height 13.75})] ;; (A) Valid, five distinct values: they come back, so the struct crosses ;; intact in both directions and raylib stored it rather than defaulting. (rl/set-shapes-texture (rl/Texture2D {:id 7 :width 13 :height 17 :mipmaps 2 :format 4}) rect) (show-texture (rl/get-shapes-texture)) (show-rect (rl/get-shapes-texture-rectangle)) ;; (B) id zero, everything else positive: the default 1 1 1 1 7 comes ;; back. The 7 is the only distinct field in it, so this pins `format` as ;; the last field, and the substitution happening at all pins `id` as the ;; field the guard reads. (rl/set-shapes-texture (rl/Texture2D {:id 0 :width 13 :height 17 :mipmaps 2 :format 4}) rect) (show-texture (rl/get-shapes-texture)) ;; (C) width zero, id positive: still stored, because the guard does not ;; look at the texture's width. Without this case, (B) would pass just as ;; well with `id` and `width` swapped — the zero would land in the guarded ;; slot either way. ;; ;; That is the limit of what is checkable here: nothing raylib computes ;; without a GL context reads width, height or mipmaps, so their order ;; among themselves is not pinned by this test. A swap there shows up as a ;; visibly wrong sprite in the interactive run, and nowhere else. (rl/set-shapes-texture (rl/Texture2D {:id 7 :width 0 :height 17 :mipmaps 2 :format 4}) rect) (show-texture (rl/get-shapes-texture))) 0)