;;;; 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)))))))