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.
109 lines
4.7 KiB
Plaintext
109 lines
4.7 KiB
Plaintext
;;;; raylib [core] example - input gestures
|
|
;;;;
|
|
;;;; examples/core/core_input_gestures.c. Needed `fade`, now bound.
|
|
;;;;
|
|
;;;; The C keeps `char gestureStrings[20][32]` and TextCopy's a name into the
|
|
;;;; next slot. Flan has no mutable character buffer and no way to copy into
|
|
;;;; one, but it does not need either: the names are compile-time literals, so
|
|
;;;; the log is a `[20 string]` and a slot holds the literal itself. That is
|
|
;;;; strictly better than the C — no truncation at 32 bytes, no copy — and it
|
|
;;;; is only possible because every string that ever goes into the log is
|
|
;;;; known at compile time. A log of strings the program had BUILT could not be
|
|
;;;; written at all.
|
|
;;;;
|
|
;;;; The C's `switch (currentGesture)` over the ten gesture values is a `cond`
|
|
;;;; of keyword equalities. `get-gesture-detected` answers an `rl/Gesture` and
|
|
;;;; `(= g :tap)` compares against a member by name, checked at compile time —
|
|
;;;; so a typo here is an error and the C's `default: break` has nothing to
|
|
;;;; catch.
|
|
;;;;
|
|
;;;; A slot that has not been written yet holds a zero-length string, because a
|
|
;;;; `defvar` with no initialiser is all-bytes-zero and a string is ptr+len —
|
|
;;;; a null pointer with a length of 0. draw-text draws nothing for it. That is
|
|
;;;; the C's `{ "" }` initialiser arriving by a different route, and it is why
|
|
;;;; resetting the log below only has to reset the counter.
|
|
|
|
(import rl "vendor:raylib")
|
|
|
|
(defconst screen-width 800)
|
|
(defconst screen-height 450)
|
|
|
|
(defconst max-gesture-strings 20)
|
|
|
|
(defvar gesture-log [max-gesture-strings string])
|
|
(defvar gestures-count i32)
|
|
(defvar current-gesture rl/Gesture)
|
|
(defvar last-gesture rl/Gesture)
|
|
|
|
;; The C's switch, as a function. `:else` is its `default:` — an unnamed
|
|
;; gesture logs the empty string, which draws nothing, which is what falling
|
|
;; through the C's switch without a TextCopy leaves in the slot.
|
|
(defn gesture-name [g rl/Gesture] string
|
|
(cond
|
|
(= g :tap) "GESTURE TAP"
|
|
(= g :double-tap) "GESTURE DOUBLETAP"
|
|
(= g :hold) "GESTURE HOLD"
|
|
(= g :drag) "GESTURE DRAG"
|
|
(= g :swipe-right) "GESTURE SWIPE RIGHT"
|
|
(= g :swipe-left) "GESTURE SWIPE LEFT"
|
|
(= g :swipe-up) "GESTURE SWIPE UP"
|
|
(= g :swipe-down) "GESTURE SWIPE DOWN"
|
|
(= g :pinch-in) "GESTURE PINCH IN"
|
|
(= g :pinch-out) "GESTURE PINCH OUT"
|
|
:else ""))
|
|
|
|
(defn main [] ()
|
|
(rl/init-window screen-width screen-height
|
|
"raylib [core] example - input gestures")
|
|
(defer (rl/close-window))
|
|
|
|
(rl/set-target-fps 60)
|
|
|
|
(let [touch-area (rl/Rectangle {.x 220.0 .y 10.0
|
|
.width (- (f32 screen-width) 230.0)
|
|
.height (- (f32 screen-height) 20.0)})]
|
|
(until (rl/window-should-close?)
|
|
;; Update
|
|
(set last-gesture current-gesture)
|
|
(set current-gesture (rl/get-gesture-detected))
|
|
(let [touch (rl/get-touch-position 0)]
|
|
|
|
(when (and (rl/collision-point-rec? touch touch-area)
|
|
(not (= current-gesture :none))
|
|
(not (= current-gesture last-gesture)))
|
|
(set (at gesture-log gestures-count) (gesture-name current-gesture))
|
|
(set gestures-count (+ gestures-count 1))
|
|
;; Full: start over. The stale slots below the counter are never
|
|
;; drawn, so they do not have to be cleared the way the C clears
|
|
;; them — the loop below is bounded by gestures-count.
|
|
(when (>= gestures-count max-gesture-strings)
|
|
(set gestures-count 0)))
|
|
|
|
;; Draw
|
|
(rl/with-drawing
|
|
(rl/clear-background rl/raywhite)
|
|
|
|
(rl/draw-rectangle-rec touch-area rl/gray)
|
|
(rl/draw-rectangle 225 15 (- screen-width 240) (- screen-height 30)
|
|
rl/raywhite)
|
|
|
|
(rl/draw-text "GESTURES TEST AREA" (- screen-width 270)
|
|
(- screen-height 40) 20 (rl/fade rl/gray 0.5))
|
|
|
|
(dotimes [i gestures-count]
|
|
(if (= 0 (% i 2))
|
|
(rl/draw-rectangle 10 (+ 30 (* 20 i)) 200 20
|
|
(rl/fade rl/lightgray 0.5))
|
|
(rl/draw-rectangle 10 (+ 30 (* 20 i)) 200 20
|
|
(rl/fade rl/lightgray 0.3)))
|
|
;; The newest entry in maroon and the rest in dark grey, as the C
|
|
;; has it.
|
|
(rl/draw-text (at gesture-log i) 35 (+ 36 (* 20 i)) 10
|
|
(if (< i (- gestures-count 1)) rl/darkgray rl/maroon)))
|
|
|
|
(rl/draw-rectangle-lines 10 29 200 (- screen-height 50) rl/gray)
|
|
(rl/draw-text "DETECTED GESTURES" 50 15 10 rl/gray)
|
|
|
|
(unless (= current-gesture :none)
|
|
(rl/draw-circle-v touch 30.0 rl/maroon)))))))
|