Skip to content

Instantly share code, notes, and snippets.

@danodic
Created July 7, 2024 05:57
Show Gist options
  • Save danodic/8b96cf9d99ec30df684e62f6a8e442e9 to your computer and use it in GitHub Desktop.
Save danodic/8b96cf9d99ec30df684e62f6a8e442e9 to your computer and use it in GitHub Desktop.
Janet SDL Example
# This is an example of how to use SDL in Janet using the FFI module.
# This is based on Lazy Foo's tutorial that can be found at:
# https://lazyfoo.net/tutorials/SDL/01_hello_SDL/index2.php
#
# In order to run this, you need:
# - To have libSDL2.so at the same folder where the source code is.
# - Create three separate files, as descibed by the comments below.
# - Read the FFI docs here:
# https://janet-lang.org/1.25.1/docs/ffi.html
#
# Some Notes:
# - Source code has been tested in Linux only.
# - I code in Clojure, this is my very first code written in Janet.
# It may not be idiomatic or following the language's conventions.
#
# In case you are not able to see the screen, run with -r and it will
# start the repl while the screen is running.
# File: hello-sdl.janet ---
#!/usr/bin/env janet
# Here we import the files which contain the FFI, contants and idiomatic api.
(import ./sdl :as sdl)
(import ./sdl-janet :as sdlj)
# Initializes SDL. Here we use some constants from the sdl module.
(sdlj/sdl-init! (get sdl/sdl-constants :sdl/sdl-init-video))
# This let block will create the window and paint a surface.
(let [screen-size [640 480]
background-color [255 255 255]
{:window/window window
:window/window-surface window-surface} (sdlj/sdl-create-window "SDL Tutorial" 0 0 ;screen-size)
pixel-format (get window-surface :surface/sdl-pixel-format)
fill-color (sdlj/sdl-map-rgb pixel-format ;background-color)]
(sdlj/sdl-fill-rect! (get window-surface :ptr) nil fill-color))
# File: sdl.janet ---
# This will load the SDL library into the FFI context.
(ffi/context "./libSDL2.so")
# Those constants have been copied from SDL's source code.
(def sdl-constants {:sdl/sdl-init-video 0x00000020
:sdl/sdl-window-shown 0x00000004})
# Defines the struct for the SDL surface.
(def SDL_Surface (ffi/struct :uint32 :ptr :int :int :ptr :ptr :int :ptr :ptr :ptr :int))
# We use defbind to create a binding to the C function.
# We can replace _ by - so it matches the lisp convention.
(ffi/defbind SDL-Init :uint32 [init-video :uint32])
# You can find a list of all avilable types in the FFI link mentioned above.
(ffi/defbind SDL-CreateWindow :ptr
[title :string
x :int
y :int
w :int
h :int
flags :uint32])
(ffi/defbind SDL-GetWindowSurface :ptr [window :ptr])
(ffi/defbind SDL-UpdateWindowSurface :int [window :ptr])
(ffi/defbind SDL-FillRect :int
[surface :ptr
sdl-rect :ptr
color :uint32])
(ffi/defbind SDL-MapRGB :uint32
[format :ptr
r :uint8
g :uint8
b :uint8])
# File: sdl-janet.janet ---
# Import the SDL file listed above.
(import ./sdl :as sdl)
# We access FFI structs by the index of the field in the struct.
# This is quite annoying, and it makes sense to create a mapped struct in which
# you can access fields using get and get-in.
# It receives a pointer to the FFI struct...
(defn make-sdl-surface
[ffi-ptr]
# ... and we read it to a buffer using the declared struct from the sdl module.
(let [surface (ffi/read sdl/SDL_Surface ffi-ptr)]
# Now that we have the buffer, we can access the values.
{:surface/ptr ffi-ptr
:surface/flags (get surface 0)
:surface/sdl-pixel-format (get surface 1)
:surface/w (get surface 2)
:surface/height (get surface 3)
:surface/pitch (get surface 4)
:surface/pixels (get surface 5)
:surface/userdata (get surface 6)
:surface/locked (get surface 7)
:surface/lock-data (get surface 8)
:surface/clip-rect (get surface 9)
:surface/sdl-blitmap (get surface 10)
:surface/refcount(get surface 11)}))
(defn- make-sdl-window
[window]
(let [surface (sdl/SDL-GetWindowSurface window)]
{:window/window window
:window/window-surface (make-sdl-surface surface)}))
(defn sdl-init!
[flags]
(let [processes-flags flags
status (sdl/SDL-Init flags)]
(when (< status 0)
# !!!
# I am not sure if this is the correct way of throwing an error.
# Do not use this as a reference.
# !!!
(error :sdl-janet-error/sdl-init-video))))
(defn sdl-create-window
[title pos-x pos-y width height]
(let [flags (get sdl/sdl-constants :sdl/sdl-window-shown)
window (sdl/SDL-CreateWindow title pos-x pos-y width height flags)]
(when (nil? window)
(error :sdl-janet-error/create-window))
(make-sdl-window window)))
(defn sdl-map-rgb
[pixel-format r g b]
(sdl/SDL-MapRGB pixel-format r g b))
(defn sdl-fill-rect!
[surface rect color]
(sdl/SDL-FillRect surface rect color))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment