The first ten of raylib's core list, ported. Seven new bindings and the named colour palette; nothing else was added, because a binding called by nothing is the same as not having bound it. The gaps they found are the point. No number reaches draw-text: i64->bytes answers [u8], draw-text wants a string, and nothing bridges — five of the ten wanted TextFormat and got a glyph table instead. And an enum parameter cannot be driven by a loop variable: the index is an i32, the parameter is an enum, neither converts, and a second declare-c with an i32 face is refused because one C function gets one binding. Two correct rules that compose into a wall. None of the gaps expected blocked anything: no generics, no allocator, no Vec, no escaping closure, no block-scoped defer. These are input-and-draw programs over fixed-size state, which is the shape the language has.
281 lines
13 KiB
Plaintext
281 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. That cannot go through the binding,
|
|
;; twice over:
|
|
;;
|
|
;; expected rl/GamepadAxis, found i32
|
|
;;
|
|
;; because an integer does not convert to an enum and a keyword can only name
|
|
;; one member; and a second declare-c of the same C function with an i32
|
|
;; parameter is refused as well —
|
|
;;
|
|
;; rl/get-gamepad-axis-movement and rl/get-gamepad-axis-movement-by-index
|
|
;; both bind the C function GetGamepadAxisMovement — one declare-c per C
|
|
;; function, and another Flan name for it is a defn
|
|
;;
|
|
;; — and a defn wrapper cannot change a parameter's type. So the index is
|
|
;; turned back into a member here, by hand, which is the only shape left. It
|
|
;; covers the six axes raylib names; a pad reporting more reads 0.0 for the
|
|
;; extras, and the count is clamped below so they are not drawn at all.
|
|
(defn axis-at [pad i32 index i32] f32
|
|
(cond
|
|
(= index 0) (rl/get-gamepad-axis-movement pad :left-x)
|
|
(= index 1) (rl/get-gamepad-axis-movement pad :left-y)
|
|
(= index 2) (rl/get-gamepad-axis-movement pad :right-x)
|
|
(= index 3) (rl/get-gamepad-axis-movement pad :right-y)
|
|
(= index 4) (rl/get-gamepad-axis-movement pad :left-trigger)
|
|
(= index 5) (rl/get-gamepad-axis-movement pad :right-trigger)
|
|
:else 0.0))
|
|
|
|
(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/begin-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 Unit and the `if` will not
|
|
;; typecheck — "expected i32, found Unit". 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)))
|
|
|
|
(rl/end-drawing))))
|