vendor/raylib/modes.flan: five macros over the five pairs the package binds -- with-drawing, with-mode-2d, with-mode-3d, with-texture-mode, with-scissor-mode. A second file with no declare-c in it, split out on vector.flan's reasoning: raylib.flan is the package's statement about C and nothing here names C, so nothing here can be made wrong by raylib changing. Each expands to (do (begin-... args) body... (end-...)) -- the calls the author used to type, in the order they typed them. No let, no gensym: nothing binds a name, so there is nothing for a caller's name to collide with. What it removes is the End* that is missing, wrong, or no longer beside its Begin*. What it cannot remove is a body leaving through the unwind path: a return or an invoke-restart skips the rest of the do and the End* with it. defer is the obvious fix and is refused inside a loop body, which is where a pair always lives -- checked, not assumed. So sand.flan's discipline stays: keep the restart boundary outside the pair. 35 call sites converted across examples/ and sand.flan. The one left is core-scissor-test.flan, whose Begin and End sit in two separate `when`s with the drawing between them -- a conditional pair is a shape a bracketing macro cannot express. test/programs/rl-with.flan covers with-scissor-mode, which no example can, with a frame function unreachable from main so it needs no libraylib; rl-with-reject.flan is the arity half.
279 lines
13 KiB
Plaintext
279 lines
13 KiB
Plaintext
;;;; raylib [core] example - input gamepad
|
|
;;;;
|
|
;;;; examples/core/core_input_gamepad.c. Needed set-config-flags (and the
|
|
;;;; FLAG_ constants), both now bound.
|
|
;;;;
|
|
;;;; Three deliberate differences from the C, all of them things the port
|
|
;;;; could not or should not follow.
|
|
;;;;
|
|
;;;; **The pad artwork is gone, and with it the xbox/PS branches.** The C draws
|
|
;;;; `resources/ps3.png` or `resources/xbox.png` and picks between them by
|
|
;;;; matching `GetGamepadName` against four substrings. Neither image is in
|
|
;;;; this repository, and importing two binaries for one example is worse than
|
|
;;;; the alternative: the C has a third branch, for a pad it does not
|
|
;;;; recognise, which draws the whole thing out of rounded rectangles and
|
|
;;;; circles and needs no asset at all. That branch is what this ports, in
|
|
;;;; full. It is also the branch a Linux desktop usually takes anyway.
|
|
;;;;
|
|
;;;; **GetGamepadName is not bound, and cannot be.** It is what the C matches
|
|
;;;; on, and declare-c refuses it by name:
|
|
;;;;
|
|
;;;; the return type of get-gamepad-name is a string, and a string only
|
|
;;;; crosses as a parameter — a C function that *returns* one returns
|
|
;;;; something Flan has no owner for
|
|
;;;;
|
|
;;;; which is correct: raylib hands back a pointer into its own static storage
|
|
;;;; and Flan has nothing that owns a borrowed C string. So the pad's name is
|
|
;;;; not on screen and the branch that used it is not here.
|
|
;;;;
|
|
;;;; **The VIBRATE button is drawn and inert.** SetGamepadVibration is the one
|
|
;;;; call in the C this refuses to bind, and vendor/raylib/raylib.flan already
|
|
;;;; carries the paragraph saying why: its arity differs between raylib
|
|
;;;; versions with no 5.5 header here to settle it, so a guess is a corrupted
|
|
;;;; stack frame rather than a link error — and the symbol in libraylib.so.550
|
|
;;;; disassembles to a single TraceLog call and a jump. Binding it would be
|
|
;;;; binding a warning. The button is drawn because taking it out would hide
|
|
;;;; the finding; it is labelled so.
|
|
;;;;
|
|
;;;; With no pad attached this shows "GP0: NOT DETECTED" and nothing else,
|
|
;;;; which is the C's behaviour minus the greyed-out artwork.
|
|
|
|
(import rl "vendor:raylib")
|
|
(import d "digits.flan")
|
|
|
|
(defconst screen-width 800)
|
|
(defconst screen-height 450)
|
|
|
|
(defconst stick-deadzone f32 0.1)
|
|
(defconst trigger-deadzone f32 -0.9)
|
|
|
|
(defvar gamepad i32)
|
|
|
|
;; The C's deadzone test, which is a band around zero and not a clamp: inside
|
|
;; it the axis reads exactly 0, outside it the raw value passes through
|
|
;; unscaled. That is a step at the edge of the band and the C has it too.
|
|
(defn deadzone [v f32 limit f32] f32
|
|
(if (and (> v (- 0.0 limit)) (< v limit)) 0.0 v))
|
|
|
|
;; The triggers rest at -1 rather than 0, which raylib does not normalise —
|
|
;; see the note on GamepadAxis in vendor/raylib/raylib.flan. So their deadzone
|
|
;; is a floor near the resting end and not a band around the middle.
|
|
(defn trigger-deadzoned [v f32] f32
|
|
(if (< v trigger-deadzone) -1.0 v))
|
|
|
|
;; The C's read-out loop is `for (i = 0; i < GetGamepadAxisCount(gamepad); i++)
|
|
;; DrawText(TextFormat("AXIS %i: %.02f", i, GetGamepadAxisMovement(gamepad, i)))`
|
|
;; — an axis selected by a loop variable, which is what this is: an integer
|
|
;; converts to an enum when the conversion is written by name.
|
|
;;
|
|
;; It used to be a six-armed cond over the members, because an enum is its own
|
|
;; type in the checker and a bare i32 does not fit one:
|
|
;;
|
|
;; expected rl/GamepadAxis, found i32
|
|
;;
|
|
;; That rule is unchanged and should be. It is what makes a misspelled
|
|
;; `:lft-x` an error *here*, at the call site, rather than a wrong axis read
|
|
;; later — which is the whole reason an enum is a type at all rather than a
|
|
;; pile of i32 constants. What changed is that the conversion can be said out
|
|
;; loud: the rule was never "an integer is dangerous", it was "an integer must
|
|
;; not arrive silently", and (rl/GamepadAxis index) is not silent.
|
|
;;
|
|
;; (The other escape stays closed, and should: a second declare-c of
|
|
;; GetGamepadAxisMovement with an i32 face is still refused — one declare-c
|
|
;; per C function — and a defn wrapper still cannot retype a parameter. It is
|
|
;; no longer needed.)
|
|
;;
|
|
;; An index raylib does not name converts too, rather than being refused, and
|
|
;; raylib bounds-checks its own axis and answers 0.0. So the (min 6 ...) clamp
|
|
;; at the call site is what keeps the read-out to the six axes raylib names —
|
|
;; load-bearing now rather than cosmetic, since it replaced the :else arm.
|
|
(defn axis-at [pad i32 index i32] f32
|
|
(rl/get-gamepad-axis-movement pad (rl/GamepadAxis index)))
|
|
|
|
(defn draw-pad-background [] ()
|
|
(rl/draw-rectangle-rounded
|
|
(rl/Rectangle {.x 175.0 .y 110.0 .width 460.0 .height 220.0})
|
|
0.3 16 rl/darkgray)
|
|
|
|
;; The three middle buttons and the four face buttons, as outlines. The
|
|
;; filled overlays go on top only while the button is down.
|
|
(rl/draw-circle 365 170 12.0 rl/raywhite)
|
|
(rl/draw-circle 405 170 12.0 rl/raywhite)
|
|
(rl/draw-circle 445 170 12.0 rl/raywhite)
|
|
(rl/draw-circle 516 191 17.0 rl/raywhite)
|
|
(rl/draw-circle 551 227 17.0 rl/raywhite)
|
|
(rl/draw-circle 587 191 17.0 rl/raywhite)
|
|
(rl/draw-circle 551 155 17.0 rl/raywhite)
|
|
|
|
;; The d-pad cross: a light plate with a dark one inset, so a pressed
|
|
;; direction has something to show up against.
|
|
(rl/draw-rectangle 245 145 28 88 rl/raywhite)
|
|
(rl/draw-rectangle 215 174 88 29 rl/raywhite)
|
|
(rl/draw-rectangle 247 147 24 84 rl/black)
|
|
(rl/draw-rectangle 217 176 84 25 rl/black)
|
|
|
|
(rl/draw-rectangle-rounded
|
|
(rl/Rectangle {.x 215.0 .y 98.0 .width 100.0 .height 10.0})
|
|
0.5 16 rl/darkgray)
|
|
(rl/draw-rectangle-rounded
|
|
(rl/Rectangle {.x 495.0 .y 98.0 .width 100.0 .height 10.0})
|
|
0.5 16 rl/darkgray))
|
|
|
|
(defn draw-pad-buttons [] ()
|
|
(when (rl/gamepad-button-down? gamepad :middle-left)
|
|
(rl/draw-circle 365 170 10.0 rl/red))
|
|
(when (rl/gamepad-button-down? gamepad :middle)
|
|
(rl/draw-circle 405 170 10.0 rl/green))
|
|
(when (rl/gamepad-button-down? gamepad :middle-right)
|
|
(rl/draw-circle 445 170 10.0 rl/blue))
|
|
(when (rl/gamepad-button-down? gamepad :right-face-left)
|
|
(rl/draw-circle 516 191 15.0 rl/gold))
|
|
(when (rl/gamepad-button-down? gamepad :right-face-down)
|
|
(rl/draw-circle 551 227 15.0 rl/blue))
|
|
(when (rl/gamepad-button-down? gamepad :right-face-right)
|
|
(rl/draw-circle 587 191 15.0 rl/green))
|
|
(when (rl/gamepad-button-down? gamepad :right-face-up)
|
|
(rl/draw-circle 551 155 15.0 rl/red))
|
|
|
|
(when (rl/gamepad-button-down? gamepad :left-face-up)
|
|
(rl/draw-rectangle 247 147 24 29 rl/red))
|
|
(when (rl/gamepad-button-down? gamepad :left-face-down)
|
|
(rl/draw-rectangle 247 201 24 30 rl/red))
|
|
(when (rl/gamepad-button-down? gamepad :left-face-left)
|
|
(rl/draw-rectangle 217 176 30 25 rl/red))
|
|
(when (rl/gamepad-button-down? gamepad :left-face-right)
|
|
(rl/draw-rectangle 271 176 30 25 rl/red))
|
|
|
|
(when (rl/gamepad-button-down? gamepad :left-trigger-1)
|
|
(rl/draw-rectangle-rounded
|
|
(rl/Rectangle {.x 215.0 .y 98.0 .width 100.0 .height 10.0})
|
|
0.5 16 rl/red))
|
|
(when (rl/gamepad-button-down? gamepad :right-trigger-1)
|
|
(rl/draw-rectangle-rounded
|
|
(rl/Rectangle {.x 495.0 .y 98.0 .width 100.0 .height 10.0})
|
|
0.5 16 rl/red)))
|
|
|
|
(defn draw-stick [cx i32 cy i32 ax f32 ay f32 thumb-down bool] ()
|
|
(rl/draw-circle cx cy 40.0 rl/black)
|
|
(rl/draw-circle cx cy 35.0 rl/lightgray)
|
|
(rl/draw-circle (+ cx (i32 (* ax 20.0))) (+ cy (i32 (* ay 20.0))) 25.0
|
|
(if thumb-down rl/red rl/black)))
|
|
|
|
(defn main [] ()
|
|
;; Before init-window, and it has to be: raylib reads the flags while it is
|
|
;; creating the context, so the same call afterwards is accepted, logged, and
|
|
;; has no effect on the window that already exists.
|
|
(rl/set-config-flags rl/flag-msaa-4x-hint)
|
|
|
|
(rl/init-window screen-width screen-height
|
|
"raylib [core] example - input gamepad")
|
|
(defer (rl/close-window))
|
|
|
|
(rl/set-target-fps 60)
|
|
|
|
(until (rl/window-should-close?)
|
|
;; Update
|
|
(when (and (rl/key-pressed? :left) (> gamepad 0))
|
|
(set gamepad (- gamepad 1)))
|
|
(when (rl/key-pressed? :right) (set gamepad (+ gamepad 1)))
|
|
|
|
(let [axis-count (min 6 (rl/get-gamepad-axis-count gamepad))
|
|
vibrate-rect (rl/Rectangle {.x 10.0
|
|
.y (+ 90.0 (* 20.0 (f32 axis-count)))
|
|
.width 75.0 .height 24.0})]
|
|
|
|
;; Draw
|
|
(rl/with-drawing
|
|
(rl/clear-background rl/raywhite)
|
|
|
|
(if (rl/gamepad-available? gamepad)
|
|
(do
|
|
;; The C draws `TextFormat("GP%d: %s", gamepad, GetGamepadName(...))`.
|
|
;; The name is unbindable — see the header — so this is the index and
|
|
;; the word the C would have followed it with.
|
|
(rl/draw-text "GP" 10 10 10 rl/black)
|
|
(let [x (+ 10 (rl/measure-text "GP" 10))]
|
|
(rl/draw-text ": CONNECTED (name unbindable)"
|
|
(+ x (d/draw-int gamepad x 10 10 rl/black))
|
|
10 10 rl/black))
|
|
|
|
(let [lx (deadzone (rl/get-gamepad-axis-movement gamepad :left-x)
|
|
stick-deadzone)
|
|
ly (deadzone (rl/get-gamepad-axis-movement gamepad :left-y)
|
|
stick-deadzone)
|
|
rx (deadzone (rl/get-gamepad-axis-movement gamepad :right-x)
|
|
stick-deadzone)
|
|
ry (deadzone (rl/get-gamepad-axis-movement gamepad :right-y)
|
|
stick-deadzone)
|
|
lt (trigger-deadzoned
|
|
(rl/get-gamepad-axis-movement gamepad :left-trigger))
|
|
rt (trigger-deadzoned
|
|
(rl/get-gamepad-axis-movement gamepad :right-trigger))]
|
|
|
|
(draw-pad-background)
|
|
(draw-pad-buttons)
|
|
|
|
(draw-stick 345 260 lx ly
|
|
(rl/gamepad-button-down? gamepad :left-thumb))
|
|
(draw-stick 465 260 rx ry
|
|
(rl/gamepad-button-down? gamepad :right-thumb))
|
|
|
|
;; The triggers as bars filling upward from a grey track. The +1
|
|
;; and the halving turn raylib's [-1, 1] into [0, 1].
|
|
(rl/draw-rectangle 151 110 15 70 rl/gray)
|
|
(rl/draw-rectangle 644 110 15 70 rl/gray)
|
|
(rl/draw-rectangle 151 110 15 (i32 (* (/ (+ 1.0 lt) 2.0) 70.0))
|
|
rl/red)
|
|
(rl/draw-rectangle 644 110 15 (i32 (* (/ (+ 1.0 rt) 2.0) 70.0))
|
|
rl/red))
|
|
|
|
(rl/draw-text "DETECTED AXIS [" 10 50 10 rl/maroon)
|
|
(let [x (+ 10 (rl/measure-text "DETECTED AXIS [" 10))]
|
|
(rl/draw-text "]:" (+ x (d/draw-int axis-count x 50 10 rl/maroon))
|
|
50 10 rl/maroon))
|
|
|
|
(dotimes [i axis-count]
|
|
(rl/draw-text "AXIS " 20 (+ 70 (* 20 i)) 10 rl/darkgray)
|
|
(let [x (+ 20 (rl/measure-text "AXIS " 10))
|
|
y (+ 70 (* 20 i))]
|
|
(set x (+ x (d/draw-int i x y 10 rl/darkgray)))
|
|
(rl/draw-text ": " x y 10 rl/darkgray)
|
|
(set x (+ x (rl/measure-text ": " 10)))
|
|
;; The raw reading, not the deadzoned one — the C shows the raw
|
|
;; value here too, which is what makes the deadzone visible as a
|
|
;; difference between this row and the stick.
|
|
(d/draw-f32 (axis-at gamepad i) 2 x y 10
|
|
rl/darkgray)))
|
|
|
|
;; Drawn and inert. See the header: SetGamepadVibration is a stub in
|
|
;; this raylib and is not bound.
|
|
(rl/draw-rectangle-rec vibrate-rect rl/skyblue)
|
|
(rl/draw-text "VIBRATE" (+ (i32 (.x vibrate-rect)) 14)
|
|
(+ (i32 (.y vibrate-rect)) 1) 10 rl/darkgray)
|
|
(rl/draw-text "(not bound: raylib stub)" 95
|
|
(+ (i32 (.y vibrate-rect)) 7) 10 rl/gray)
|
|
|
|
;; -1 when nothing is pressed, which is why the binding answers an
|
|
;; i32 and not a GamepadButton.
|
|
;; draw-int answers the width it drew, so a branch that ends in one
|
|
;; has type i32 while its sibling has type () and the `if` will not
|
|
;; typecheck — "expected i32, found ()". Both arms end in a
|
|
;; draw-text here, which is the tidy way out; where that is awkward a
|
|
;; trailing `(do)` is the other.
|
|
(let [b (rl/get-gamepad-button-pressed)]
|
|
(if (>= b 0)
|
|
(do (rl/draw-text "DETECTED BUTTON: " 10 430 10 rl/red)
|
|
(d/draw-int b (+ 10 (rl/measure-text "DETECTED BUTTON: " 10))
|
|
430 10 rl/red)
|
|
(do))
|
|
(rl/draw-text "DETECTED BUTTON: NONE" 10 430 10 rl/gray))))
|
|
|
|
(do
|
|
(rl/draw-text "GP" 10 10 10 rl/gray)
|
|
(let [x (+ 10 (rl/measure-text "GP" 10))]
|
|
(rl/draw-text ": NOT DETECTED"
|
|
(+ x (d/draw-int gamepad x 10 10 rl/gray))
|
|
10 10 rl/gray))
|
|
(rl/draw-text "left/right arrows select another pad" 10 30 10
|
|
rl/lightgray)))))))
|