GameLib is a collection of libraries for creating applications in Cakelisp.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

724 lines
32 KiB

(set-cakelisp-option cakelisp-src-dir "Dependencies/cakelisp/src")
(add-cakelisp-search-directory "Dependencies/cakelisp/runtime")
(import "CHelpers.cake" "BuildTools.cake" "Dependencies.cake")
(c-import "SDL.h" "SDL_syswm.h"))
(c-import "<stdio.h>" &with-decls "<stddef.h>")
;; Core/windowing
(forward-declare (struct SDL_Window)
(struct SDL_AudioSpec))
(defun sdl-print-error ()
(fprintf stderr "SDL_Error: %s\n" (SDL_GetError)))
;; This supports drawing using SDL functions
(defun sdl-initialize-for-2d (window-out (* (* SDL_Window))
title (* (const char))
width int height int
&return bool)
(return false))
(set (deref window-out)
(SDL_CreateWindow title
width height
(unless (deref window-out)
(return false))
(return true))
;; This is the OpenGL version, which may not support SDL drawing functions, only OpenGL
(defun sdl-initialize-for-3d (window-out (* (* SDL_Window))
title (* (const char))
width int height int
&return bool)
(return false))
;; This is necessary to make sure the context is created using a newer version. I mainly did this
;; because RenderDoc said it needed it. This version comes from my current machine's version, and
;; isn't a requirement to be this high
(set (deref window-out)
(SDL_CreateWindow title
(unless (deref window-out)
(return false))
;; Must explicitly create the GL context for Ogre
(unless (SDL_GL_CreateContext (deref window-out))
(return false))
(return true))
(defun sdl-shutdown (window (* SDL_Window))
(SDL_DestroyWindow window)
;; Graphics
(defun sdl-list-2d-render-drivers (&return bool)
(var num-render-drivers int (SDL_GetNumRenderDrivers))
(unless num-render-drivers
(return false))
(var i int 0)
num-render-drivers i
(var driver-info SDL_RendererInfo (array 0))
(unless (= 0 (SDL_GetRenderDriverInfo i (addr driver-info)))
(return false))
(SDL_Log "Renderer [%d]: %s\n
\tHardware accelerated: %s\n
\tRender to texture: %s\n
\tMax texture width: %d\n
\tMax texture height: %d\n
i (field driver-info name)
(? (bit-and (field driver-info flags) SDL_RENDERER_ACCELERATED) "yes" "no")
(? (bit-and (field driver-info flags) SDL_RENDERER_TARGETTEXTURE) "yes" "no")
(field driver-info max_texture_width)
(field driver-info max_texture_height)))
(return true))
(defun-local sdl-texture-from-bmp (filename (* (const char)) renderer (* SDL_Renderer)
&return (* SDL_Texture))
(var surface (* SDL_Surface) (SDL_LoadBMP filename))
(unless surface
(SDL_Log "Failed to load surface from BMP %s\n" filename)
(return null))
(var texture (* SDL_Texture)
(SDL_CreateTextureFromSurface renderer surface))
;; No need to hold on to surface after texture has been created
(SDL_FreeSurface surface)
(unless texture (sdl-print-error))
(return texture))
(defun-local sdl-texture-from-bmp-color-to-transparent
(filename (* (const char)) renderer (* SDL_Renderer) r char g char b char
&return (* SDL_Texture))
(var surface (* SDL_Surface) (SDL_LoadBMP filename))
(unless surface
(SDL_Log "Failed to load surface from BMP %s\n" filename)
(return null))
(SDL_SetColorKey surface SDL_TRUE (SDL_MapRGB (path surface > format) r g b))
(var texture (* SDL_Texture)
(SDL_CreateTextureFromSurface renderer surface))
;; No need to hold on to surface after texture has been created
(SDL_FreeSurface surface)
(unless texture (sdl-print-error))
(return texture))
;; Audio
;; Allocates both names and array
;; Returns number of devices in device-names-out
(defun sdl-audio-get-devices (device-names-out (* (* (* (const char))))
is-capture bool &return int)
(var num-devices int (SDL_GetNumAudioDevices is-capture))
(set (deref device-names-out) (type-cast
(calloc (sizeof (type (* (const char)))) num-devices)
(* (* (const char)))))
(fprintf stderr "Available %s devices:\n" (? is-capture "recording" "playback"))
(var i int 0)
(while (< i num-devices)
(var device-name (* (const char)) (SDL_GetAudioDeviceName i is-capture))
(when device-name
(fprintf stderr "\t[%d] %s\n" i device-name)
(set (at i (deref device-names-out)) (strdup device-name)))
(incr i))
(return num-devices))
(defun sdl-audio-list-specification (spec (* SDL_AudioSpec))
(fprintf stderr "freq: %d\n" (path spec > freq))
(fprintf stderr "format: %d\n" (path spec > format))
(fprintf stderr "channels: %d\n" (path spec > channels))
(fprintf stderr "samples: %d\n" (path spec > samples)))
(defun sdl-audio-free-device-list (device-names (* (* (const char))) num-devices int)
(var i int 0)
(while (< i num-devices)
(free (type-cast (at i device-names) (* void)))
(incr i))
(free device-names))
;; Time
;; Useful for getting a quick idea how long something takes, e.g.:
;; (var start-load-ticks (const Uint64) (SDL_GetPerformanceFrequency))
;; (do-load)
;; (sdl-print-time-delta start-load-ticks "Loading done")
;; ...Will print e.g. "--- Loading done at 0.94 seconds"
(defun-local sdl-print-time-delta (start-num-perf-ticks Uint64 label (* (const char)))
(var performance-num-ticks-per-second (const Uint64) (SDL_GetPerformanceFrequency))
(var current-counter-ticks Uint64 (SDL_GetPerformanceCounter))
(var frame-diff-ticks Uint64 (- current-counter-ticks start-num-perf-ticks))
(var delta-time float (/ frame-diff-ticks
(type-cast performance-num-ticks-per-second float)))
(SDL_Log "--- %s at %f seconds\n" label delta-time))
;; This should be thread-safe assuming set-startup-time-now is only called once
(var s-startup-num-perf-ticks Uint64 0)
(defun set-startup-time-now ()
(set s-startup-num-perf-ticks (SDL_GetPerformanceCounter)))
(defun get-time-since-startup (&return float)
(var performance-num-ticks-per-second (const Uint64) (SDL_GetPerformanceFrequency))
(var current-counter-ticks Uint64 (SDL_GetPerformanceCounter))
(var frame-diff-ticks Uint64 (- current-counter-ticks s-startup-num-perf-ticks))
(var delta-time float (/ frame-diff-ticks
(type-cast performance-num-ticks-per-second float)))
(return delta-time))
;; Input
(defun sdl-is-key-tapped-this-frame (key (unsigned int)
current-key-states (* (const (unsigned char)))
last-frame-key-states (* (const (unsigned char)))
&return bool)
(return (and key
(at key current-key-states)
(not (at key last-frame-key-states)))))
(defun sdl-scancode-to-user-string (code (unsigned int) &return (* (const char)))
;; TODO: Would be faster in switch, possibly
((= code SDL_SCANCODE_UNKNOWN) (return "Unknown"))
((= code SDL_SCANCODE_A) (return "A"))
((= code SDL_SCANCODE_B) (return "B"))
((= code SDL_SCANCODE_C) (return "C"))
((= code SDL_SCANCODE_D) (return "D"))
((= code SDL_SCANCODE_E) (return "E"))
((= code SDL_SCANCODE_F) (return "F"))
((= code SDL_SCANCODE_G) (return "G"))
((= code SDL_SCANCODE_H) (return "H"))
((= code SDL_SCANCODE_I) (return "I"))
((= code SDL_SCANCODE_J) (return "J"))
((= code SDL_SCANCODE_K) (return "K"))
((= code SDL_SCANCODE_L) (return "L"))
((= code SDL_SCANCODE_M) (return "M"))
((= code SDL_SCANCODE_N) (return "N"))
((= code SDL_SCANCODE_O) (return "O"))
((= code SDL_SCANCODE_P) (return "P"))
((= code SDL_SCANCODE_Q) (return "Q"))
((= code SDL_SCANCODE_R) (return "R"))
((= code SDL_SCANCODE_S) (return "S"))
((= code SDL_SCANCODE_T) (return "T"))
((= code SDL_SCANCODE_U) (return "U"))
((= code SDL_SCANCODE_V) (return "V"))
((= code SDL_SCANCODE_W) (return "W"))
((= code SDL_SCANCODE_X) (return "X"))
((= code SDL_SCANCODE_Y) (return "Y"))
((= code SDL_SCANCODE_Z) (return "Z"))
((= code SDL_SCANCODE_1) (return "1"))
((= code SDL_SCANCODE_2) (return "2"))
((= code SDL_SCANCODE_3) (return "3"))
((= code SDL_SCANCODE_4) (return "4"))
((= code SDL_SCANCODE_5) (return "5"))
((= code SDL_SCANCODE_6) (return "6"))
((= code SDL_SCANCODE_7) (return "7"))
((= code SDL_SCANCODE_8) (return "8"))
((= code SDL_SCANCODE_9) (return "9"))
((= code SDL_SCANCODE_0) (return "0"))
((= code SDL_SCANCODE_RETURN) (return "Return"))
((= code SDL_SCANCODE_ESCAPE) (return "Escape"))
((= code SDL_SCANCODE_BACKSPACE) (return "Backspace"))
((= code SDL_SCANCODE_TAB) (return "Tab"))
((= code SDL_SCANCODE_SPACE) (return "Space"))
((= code SDL_SCANCODE_MINUS) (return "Minus"))
((= code SDL_SCANCODE_EQUALS) (return "Equals"))
((= code SDL_SCANCODE_LEFTBRACKET) (return "["))
((= code SDL_SCANCODE_RIGHTBRACKET) (return "]"))
((= code SDL_SCANCODE_BACKSLASH) (return "Backslash"))
((= code SDL_SCANCODE_SEMICOLON) (return ";"))
((= code SDL_SCANCODE_APOSTROPHE) (return "'"))
((= code SDL_SCANCODE_GRAVE) (return "`"))
((= code SDL_SCANCODE_COMMA) (return ","))
((= code SDL_SCANCODE_PERIOD) (return "."))
((= code SDL_SCANCODE_SLASH) (return "/"))
((= code SDL_SCANCODE_CAPSLOCK) (return "Capslock"))
((= code SDL_SCANCODE_F1) (return "F1"))
((= code SDL_SCANCODE_F2) (return "F2"))
((= code SDL_SCANCODE_F3) (return "F3"))
((= code SDL_SCANCODE_F4) (return "F4"))
((= code SDL_SCANCODE_F5) (return "F5"))
((= code SDL_SCANCODE_F6) (return "F6"))
((= code SDL_SCANCODE_F7) (return "F7"))
((= code SDL_SCANCODE_F8) (return "F8"))
((= code SDL_SCANCODE_F9) (return "F9"))
((= code SDL_SCANCODE_F10) (return "F10"))
((= code SDL_SCANCODE_F11) (return "F11"))
((= code SDL_SCANCODE_F12) (return "F12"))
((= code SDL_SCANCODE_PRINTSCREEN) (return "Print screen"))
((= code SDL_SCANCODE_SCROLLLOCK) (return "Scroll lock"))
((= code SDL_SCANCODE_PAUSE) (return "Pause"))
((= code SDL_SCANCODE_INSERT) (return "Insert"))
((= code SDL_SCANCODE_HOME) (return "Home"))
((= code SDL_SCANCODE_PAGEUP) (return "Page Up"))
((= code SDL_SCANCODE_DELETE) (return "Delete"))
((= code SDL_SCANCODE_END) (return "End"))
((= code SDL_SCANCODE_PAGEDOWN) (return "Page Down"))
((= code SDL_SCANCODE_RIGHT) (return "Right"))
((= code SDL_SCANCODE_LEFT) (return "Left"))
((= code SDL_SCANCODE_DOWN) (return "Down"))
((= code SDL_SCANCODE_UP) (return "Up"))
((= code SDL_SCANCODE_NUMLOCKCLEAR) (return "Num lock clear"))
((= code SDL_SCANCODE_KP_DIVIDE) (return "Keypad /"))
((= code SDL_SCANCODE_KP_MULTIPLY) (return "Keypad *"))
((= code SDL_SCANCODE_KP_MINUS) (return "Keypad -"))
((= code SDL_SCANCODE_KP_PLUS) (return "Keypad +"))
((= code SDL_SCANCODE_KP_ENTER) (return "Keypad Enter"))
((= code SDL_SCANCODE_KP_1) (return "Keypad 1"))
((= code SDL_SCANCODE_KP_2) (return "Keypad 2"))
((= code SDL_SCANCODE_KP_3) (return "Keypad 3"))
((= code SDL_SCANCODE_KP_4) (return "Keypad 4"))
((= code SDL_SCANCODE_KP_5) (return "Keypad 5"))
((= code SDL_SCANCODE_KP_6) (return "Keypad 6"))
((= code SDL_SCANCODE_KP_7) (return "Keypad 7"))
((= code SDL_SCANCODE_KP_8) (return "Keypad 8"))
((= code SDL_SCANCODE_KP_9) (return "Keypad 9"))
((= code SDL_SCANCODE_KP_0) (return "Keypad 0"))
((= code SDL_SCANCODE_KP_PERIOD) (return "Keypad ."))
((= code SDL_SCANCODE_APPLICATION) (return "Application"))
((= code SDL_SCANCODE_POWER) (return "Power"))
((= code SDL_SCANCODE_KP_EQUALS) (return "Keypad ="))
((= code SDL_SCANCODE_F13) (return "F13"))
((= code SDL_SCANCODE_F14) (return "F14"))
((= code SDL_SCANCODE_F15) (return "F15"))
((= code SDL_SCANCODE_F16) (return "F16"))
((= code SDL_SCANCODE_F17) (return "F17"))
((= code SDL_SCANCODE_F18) (return "F18"))
((= code SDL_SCANCODE_F19) (return "F19"))
((= code SDL_SCANCODE_F20) (return "F20"))
((= code SDL_SCANCODE_F21) (return "F21"))
((= code SDL_SCANCODE_F22) (return "F22"))
((= code SDL_SCANCODE_F23) (return "F23"))
((= code SDL_SCANCODE_F24) (return "F24"))
((= code SDL_SCANCODE_EXECUTE) (return "Execute"))
((= code SDL_SCANCODE_HELP) (return "Help"))
((= code SDL_SCANCODE_MENU) (return "Menu"))
((= code SDL_SCANCODE_SELECT) (return "Select"))
((= code SDL_SCANCODE_STOP) (return "Stop"))
((= code SDL_SCANCODE_AGAIN) (return "Again"))
((= code SDL_SCANCODE_UNDO) (return "Undo"))
((= code SDL_SCANCODE_CUT) (return "Cut"))
((= code SDL_SCANCODE_COPY) (return "Copy"))
((= code SDL_SCANCODE_PASTE) (return "Paste"))
((= code SDL_SCANCODE_FIND) (return "Find"))
((= code SDL_SCANCODE_MUTE) (return "Mute"))
((= code SDL_SCANCODE_VOLUMEUP) (return "Volume Up"))
((= code SDL_SCANCODE_VOLUMEDOWN) (return "Volume Down"))
((= code SDL_SCANCODE_KP_COMMA) (return "Keypad ,"))
((= code SDL_SCANCODE_KP_EQUALSAS400) (return "Keypad EQUALSAS400"))
((= code SDL_SCANCODE_INTERNATIONAL1) (return "International1"))
((= code SDL_SCANCODE_INTERNATIONAL2) (return "International2"))
((= code SDL_SCANCODE_INTERNATIONAL3) (return "International3"))
((= code SDL_SCANCODE_INTERNATIONAL4) (return "International4"))
((= code SDL_SCANCODE_INTERNATIONAL5) (return "International5"))
((= code SDL_SCANCODE_INTERNATIONAL6) (return "International6"))
((= code SDL_SCANCODE_INTERNATIONAL7) (return "International7"))
((= code SDL_SCANCODE_INTERNATIONAL8) (return "International8"))
((= code SDL_SCANCODE_INTERNATIONAL9) (return "International9"))
((= code SDL_SCANCODE_LANG1) (return "Lang1"))
((= code SDL_SCANCODE_LANG2) (return "Lang2"))
((= code SDL_SCANCODE_LANG3) (return "Lang3"))
((= code SDL_SCANCODE_LANG4) (return "Lang4"))
((= code SDL_SCANCODE_LANG5) (return "Lang5"))
((= code SDL_SCANCODE_LANG6) (return "Lang6"))
((= code SDL_SCANCODE_LANG7) (return "Lang7"))
((= code SDL_SCANCODE_LANG8) (return "Lang8"))
((= code SDL_SCANCODE_LANG9) (return "Lang9"))
((= code SDL_SCANCODE_ALTERASE) (return "Alt erase"))
((= code SDL_SCANCODE_SYSREQ) (return "Sysreq"))
((= code SDL_SCANCODE_CANCEL) (return "Cancel"))
((= code SDL_SCANCODE_CLEAR) (return "Clear"))
((= code SDL_SCANCODE_PRIOR) (return "Prior"))
((= code SDL_SCANCODE_RETURN2) (return "Return2"))
((= code SDL_SCANCODE_SEPARATOR) (return "Separator"))
((= code SDL_SCANCODE_OUT) (return "Out"))
((= code SDL_SCANCODE_OPER) (return "Oper"))
((= code SDL_SCANCODE_CLEARAGAIN) (return "Clearagain"))
((= code SDL_SCANCODE_CRSEL) (return "Crsel"))
((= code SDL_SCANCODE_EXSEL) (return "Exsel"))
((= code SDL_SCANCODE_KP_00) (return "Keypad 00"))
((= code SDL_SCANCODE_KP_000) (return "Keypad 000"))
((= code SDL_SCANCODE_THOUSANDSSEPARATOR) (return "Thousands separator"))
((= code SDL_SCANCODE_DECIMALSEPARATOR) (return "Decimal separator"))
((= code SDL_SCANCODE_CURRENCYUNIT) (return "Currency unit"))
((= code SDL_SCANCODE_CURRENCYSUBUNIT) (return "Currency sub-unit"))
((= code SDL_SCANCODE_KP_LEFTPAREN) (return "Keypad ("))
((= code SDL_SCANCODE_KP_RIGHTPAREN) (return "Keypad )"))
((= code SDL_SCANCODE_KP_LEFTBRACE) (return "Keypad ["))
((= code SDL_SCANCODE_KP_RIGHTBRACE) (return "Keypad ]"))
((= code SDL_SCANCODE_KP_TAB) (return "Keypad Tab"))
((= code SDL_SCANCODE_KP_BACKSPACE) (return "Keypad Backspace"))
((= code SDL_SCANCODE_KP_A) (return "Keypad A"))
((= code SDL_SCANCODE_KP_B) (return "Keypad B"))
((= code SDL_SCANCODE_KP_C) (return "Keypad C"))
((= code SDL_SCANCODE_KP_D) (return "Keypad D"))
((= code SDL_SCANCODE_KP_E) (return "Keypad E"))
((= code SDL_SCANCODE_KP_F) (return "Keypad F"))
((= code SDL_SCANCODE_KP_XOR) (return "Keypad XOR"))
((= code SDL_SCANCODE_KP_POWER) (return "Keypad Power"))
((= code SDL_SCANCODE_KP_PERCENT) (return "Keypad %"))
((= code SDL_SCANCODE_KP_LESS) (return "Keypad <"))
((= code SDL_SCANCODE_KP_GREATER) (return "Keypad >"))
((= code SDL_SCANCODE_KP_AMPERSAND) (return "Keypad &"))
((= code SDL_SCANCODE_KP_DBLAMPERSAND) (return "Keypad &&"))
((= code SDL_SCANCODE_KP_VERTICALBAR) (return "Keypad |"))
((= code SDL_SCANCODE_KP_DBLVERTICALBAR) (return "Keypad ||"))
((= code SDL_SCANCODE_KP_COLON) (return "Keypad :"))
((= code SDL_SCANCODE_KP_HASH) (return "Keypad #"))
((= code SDL_SCANCODE_KP_SPACE) (return "Keypad Space"))
((= code SDL_SCANCODE_KP_AT) (return "Keypad At"))
((= code SDL_SCANCODE_KP_EXCLAM) (return "Keypad !"))
((= code SDL_SCANCODE_KP_MEMSTORE) (return "Keypad Mem store"))
((= code SDL_SCANCODE_KP_MEMRECALL) (return "Keypad Mem recall"))
((= code SDL_SCANCODE_KP_MEMCLEAR) (return "Keypad Mem clear"))
((= code SDL_SCANCODE_KP_MEMADD) (return "Keypad Mem add"))
((= code SDL_SCANCODE_KP_MEMSUBTRACT) (return "Keypad Mem subtract"))
((= code SDL_SCANCODE_KP_MEMMULTIPLY) (return "Keypad Mem multiply"))
((= code SDL_SCANCODE_KP_MEMDIVIDE) (return "Keypad Mem divide"))
((= code SDL_SCANCODE_KP_PLUSMINUS) (return "Keypad Plus/minus"))
((= code SDL_SCANCODE_KP_CLEAR) (return "Keypad Clear"))
((= code SDL_SCANCODE_KP_CLEARENTRY) (return "Keypad Clearentry"))
((= code SDL_SCANCODE_KP_BINARY) (return "Keypad Binary"))
((= code SDL_SCANCODE_KP_OCTAL) (return "Keypad Octal"))
((= code SDL_SCANCODE_KP_DECIMAL) (return "Keypad Decimal"))
((= code SDL_SCANCODE_KP_HEXADECIMAL) (return "Keypad Hexadecimal"))
((= code SDL_SCANCODE_LCTRL) (return "Left Ctrl"))
((= code SDL_SCANCODE_LSHIFT) (return "Left Shift"))
((= code SDL_SCANCODE_LALT) (return "Left Alt"))
((= code SDL_SCANCODE_LGUI) (return "Left GUI"))
((= code SDL_SCANCODE_RCTRL) (return "Right Ctrl"))
((= code SDL_SCANCODE_RSHIFT) (return "Right Shift"))
((= code SDL_SCANCODE_RALT) (return "Right Alt"))
((= code SDL_SCANCODE_RGUI) (return "Right GUI"))
((= code SDL_SCANCODE_MODE) (return "Mode"))
((= code SDL_SCANCODE_AUDIONEXT) (return "Audio next"))
((= code SDL_SCANCODE_AUDIOPREV) (return "Audio prev"))
((= code SDL_SCANCODE_AUDIOSTOP) (return "Audio stop"))
((= code SDL_SCANCODE_AUDIOPLAY) (return "Audio play"))
((= code SDL_SCANCODE_AUDIOMUTE) (return "Audio mute"))
((= code SDL_SCANCODE_MEDIASELECT) (return "Media select"))
((= code SDL_SCANCODE_WWW) (return "www"))
((= code SDL_SCANCODE_MAIL) (return "Mail"))
((= code SDL_SCANCODE_CALCULATOR) (return "Calculator"))
((= code SDL_SCANCODE_COMPUTER) (return "Computer"))
((= code SDL_SCANCODE_AC_SEARCH) (return "AC Search"))
((= code SDL_SCANCODE_AC_HOME) (return "AC Home"))
((= code SDL_SCANCODE_AC_BACK) (return "AC Back"))
((= code SDL_SCANCODE_AC_FORWARD) (return "AC Forward"))
((= code SDL_SCANCODE_AC_STOP) (return "AC Stop"))
((= code SDL_SCANCODE_AC_REFRESH) (return "AC Refresh"))
((= code SDL_SCANCODE_AC_BOOKMARKS) (return "AC Bookmarks"))
((= code SDL_SCANCODE_BRIGHTNESSDOWN) (return "Brightness down"))
((= code SDL_SCANCODE_BRIGHTNESSUP) (return "Brightness up"))
((= code SDL_SCANCODE_DISPLAYSWITCH) (return "Display switch"))
((= code SDL_SCANCODE_KBDILLUMTOGGLE) (return "Kbd illum toggle"))
((= code SDL_SCANCODE_KBDILLUMDOWN) (return "Kbd illum down"))
((= code SDL_SCANCODE_KBDILLUMUP) (return "Kbd illum up"))
((= code SDL_SCANCODE_EJECT) (return "Eject"))
((= code SDL_SCANCODE_SLEEP) (return "Sleep"))
((= code SDL_SCANCODE_APP1) (return "App1"))
((= code SDL_SCANCODE_APP2) (return "App2"))
((= code SDL_SCANCODE_AUDIOREWIND) (return "Audio rewind"))
((= code SDL_SCANCODE_AUDIOFASTFORWARD) (return "Audio fast-forward"))
(return "Unknown"))))
;; TODO Cakelisp CHelpers support flags
(var-global keybind-modifier-flags-none (unsigned char) 0)
(var-global keybind-modifier-flags-ctrl (unsigned char) (bit-<< 1 0))
(var-global keybind-modifier-flags-shift (unsigned char) (bit-<< 1 1))
(var-global keybind-modifier-flags-alt (unsigned char) (bit-<< 1 2))
(defstruct keybind-key
key (unsigned int)
modifier-flags (unsigned char))
(defstruct keybind
bound-keys (* keybind-key)
num-binds (unsigned char))
(defstruct sdl-key-states
last-frame-states (* (const (unsigned char)))
this-frame-states (* (const (unsigned char))))
(defun keybind-tapped (keybinds (* keybind) key-states (* sdl-key-states) &return bool)
(each-in-range (path keybinds > num-binds) i
(var bind (* keybind-key) (addr (at i (path keybinds > bound-keys))))
(when (sdl-is-key-tapped-this-frame
(path bind > key)
(path key-states > this-frame-states)
(path key-states > last-frame-states))
((bit-and (path bind > modifier-flags) keybind-modifier-flags-ctrl)
(return (or (at SDL_SCANCODE_LCTRL (path key-states > this-frame-states))
(at SDL_SCANCODE_RCTRL (path key-states > this-frame-states)))))
((bit-and (path bind > modifier-flags) keybind-modifier-flags-shift)
(return (or (at SDL_SCANCODE_LSHIFT (path key-states > this-frame-states))
(at SDL_SCANCODE_RSHIFT (path key-states > this-frame-states)))))
((bit-and (path bind > modifier-flags) keybind-modifier-flags-alt)
(return (or (at SDL_SCANCODE_LALT (path key-states > this-frame-states))
(at SDL_SCANCODE_RALT (path key-states > this-frame-states)))))
(return true)))))
(return false))
(defun-local append-to-string-buffer (buffer-out (* char) buffer-size size_t write-head (* (* char))
append-str (* (const char)))
(var str-length size_t (strlen append-str))
(when (> str-length (- buffer-size (- (deref write-head) buffer-out)))
(fprintf stderr "error: Buffer was not large enough to fit string! Buffer size: %d\n"
(type-cast buffer-size int))
(strcpy (deref write-head) append-str)
(set (deref write-head) (+ (deref write-head) str-length)))
(defun keybind-get-description (buffer-out (* char) buffer-size size_t keybinds (* keybind))
(set (at 0 buffer-out) 0)
(unless (path keybinds > num-binds)
(var write-head (* char) buffer-out)
(each-in-range (path keybinds > num-binds) i
(var bind (* keybind-key) (addr (at i (path keybinds > bound-keys))))
(defstruct flag-to-modifier flag (unsigned char) text (* (const char)))
(var flag-to-modifiers ([] (const flag-to-modifier))
(array (array keybind-modifier-flags-ctrl "Ctrl+")
(array keybind-modifier-flags-shift "Shift+")
(array keybind-modifier-flags-alt "Alt+")))
(each-in-array flag-to-modifiers modifier-index
(when (bit-and (path bind > modifier-flags)
(field (at modifier-index flag-to-modifiers) flag))
(append-to-string-buffer buffer-out buffer-size (addr write-head)
(field (at modifier-index flag-to-modifiers) text))))
(append-to-string-buffer buffer-out buffer-size (addr write-head)
(sdl-scancode-to-user-string (path bind > key)))
(when (< i (- (path keybinds > num-binds) 1))
(append-to-string-buffer buffer-out buffer-size (addr write-head)
", "))))
(defmacro define-keybind (name symbol &rest binds array)
(var buttons-name Token (deref name))
(call-on append (field buttons-name contents) "-buttons")
(tokenize-push output
(var (token-splice-addr buttons-name) ([] keybind-key)
(array (token-splice-rest binds tokens)))
(var (token-splice name) keybind (array (token-splice-addr buttons-name)
(array-size (token-splice-addr buttons-name)))))
(return true))
;; Files
(defun sdl-make-app-data-path (buffer (* char) buffer-size int
company (* (const char)) application (* (const char))
path (* (const char)))
(var pref-path (* char) (SDL_GetPrefPath company application))
(snprintf buffer buffer-size "%s%s" pref-path path)
(SDL_free pref-path))
;; Test
(defun test--sdl-main (&return int)
(fprintf stderr "Hello, SDL!\n")
(var window (* SDL_Window) null)
(unless (sdl-initialize-for-2d (addr window) "GameLib" 640 480) (return 1))
;; (var window-surface (* SDL_Surface) (SDL_GetWindowSurface window))
(var exit-reason (* (const char)) null)
(while (not exit-reason)
(var event SDL_Event)
(while (SDL_PollEvent (addr event))
(when (= (field event type) SDL_QUIT)
(set exit-reason "Window event")))
(var currentKeyStates (* (const Uint8)) (SDL_GetKeyboardState null))
(when (at SDL_SCANCODE_ESCAPE currentKeyStates)
(set exit-reason "Escape pressed"))
(SDL_UpdateWindowSurface window))
(when exit-reason
(fprintf stderr "Exiting. Reason: %s\n" exit-reason))
(sdl-shutdown window)
(return 0))
;; Building
(defun-comptime build-sdl-on-failure (failure-message (* (const char)))
(Logf "error: SDL build: %s\n
Note that you can also build SDL manually. This can be useful if you are porting to a new platform
and do not want to try to automate it yet.\n
The build step will automatically detect your build, as long as it is installed to
cakelisp_cache/SDLInstallDir/[include | lib].\n
See for how to build manually.\n"
;; TODO: Build into cakelisp_cache instead of dirtying Git repo
(defun-comptime build-sdl (manager (& ModuleManager) module (* Module) &return bool)
(when (and (fileExists "Dependencies/SDL/include/SDL.h")
(fileExists "Dependencies/SDL/VisualC/x64/Debug/SDL2.dll"))
(return true))
;; Cakelisp's resolveExecutablePath() will find MSBuild for us
(var msbuild-path (* (const char)) "MSBuild.exe")
(var target-platform-version ([] 256 char) (array 0))
(makeTargetPlatformVersionArgument target-platform-version (sizeof target-platform-version))
;; TODO: Debug vs release
;; TODO: Auto detect? Environment variables: WindowsSDKVersion
"failed at SDL build step. This requires Microsoft Visual Studio to execute.\nYou may also
need to edit SDL.cake and update the PlatformToolset and SDK versions for your environment.")
(return false))
(unless (and (fileExists "Dependencies/SDL/include/SDL.h")
(fileExists "Dependencies/SDL/VisualC/x64/Debug/SDL2.dll"))
"error: SDL build sequence completed, but files are not where expected. Is there an issue
with the configuration?")
(return false)))
;; Already built?
;; We could enhance this by checking for modifications, but that's pretty rare
(when (and (fileExists "cakelisp_cache/SDLInstallDir/include/SDL2/SDL.h")
(fileExists "cakelisp_cache/SDLInstallDir/lib/libSDL2.a"))
(return true))
(Log "SDL: Building via Configure and Make\n")
(var sdl-working-dir (* (const char)) "cakelisp_cache/SDLBuildDir")
(makeDirectory sdl-working-dir)
(var sdl-output-dir (* (const char)) "cakelisp_cache/SDLInstallDir")
(makeDirectory sdl-output-dir)
(var configure-output-prefix ([] MAX_PATH_LENGTH char) (array 0))
(scope ;; Output must be absolute directory
(var absolute-output-path (* (const char))
(makeAbsolutePath_Allocated null sdl-output-dir))
(unless absolute-output-path
(Logf "error: failed to make SDL output directory '%s'\n" sdl-output-dir)
(return false))
(PrintfBuffer configure-output-prefix "--prefix=%s" absolute-output-path)
(free (type-cast absolute-output-path (* void))))
("sh" "../../Dependencies/SDL/configure" configure-output-prefix :in-directory sdl-working-dir)
"failed at SDL configure step. This requires a sh/bash-style shell to execute.")
(return false))
("make" "--jobs=8" :in-directory sdl-working-dir)
(build-sdl-on-failure "failed at SDL make. This tool requires Makefile support.")
(return false))
("make" "install" :in-directory sdl-working-dir)
"failed at SDL make install. Was there a configuration issue with --prefix?")
(return false))
;; One final to check to ensure everything's good to go
(unless (and (fileExists "cakelisp_cache/SDLInstallDir/include/SDL2/SDL.h")
(fileExists "cakelisp_cache/SDLInstallDir/lib/libSDL2.a"))
"error: SDL build sequence completed, but files are not where expected. Is there an issue
with the configuration?\nFiles are expected in:\n\tcakelisp_cache/SDLInstallDir/include
(return false)))
(comptime-error "need to define platform, e.g. (comptime-define-symbol 'Unix), or your platform
has not been implemented yet. See to add your platform.")))
(Log "SDL: Successfully built\n")
(return true))
(add-dependency-git-submodule clone-sdl2 "" "Dependencies/SDL")
(add-compile-time-hook-module pre-build build-sdl)
(add-c-search-directory-module "Dependencies/SDL/include"))
(add-c-search-directory-module "cakelisp_cache/SDLInstallDir/include/SDL2"))))
;; TODO: Add debug version
(add-library-search-directory "Dependencies/SDL/VisualC/x64/Debug")
(add-static-link-objects "SDL2main.lib" "SDL2.lib"))
;; (add-library-runtime-search-directory "Dependencies/SDL/VisualC/x64/Debug" "."))
('Unix ;; Static link
(add-compiler-link-options "-pthread")
(add-library-search-directory "cakelisp_cache/SDLInstallDir/lib")
(add-library-dependency "SDL2")
;; TODO: Relative path is going to break for sure
(add-library-runtime-search-directory "cakelisp_cache/SDLInstallDir/lib" "."))
(add-static-link-objects "cakelisp_cache/SDLInstallDir/lib/libSDL2.a")))))
('Windows ;; DLLs need to be in the same directory as the executable
(defun-comptime copy-sdl2-dlls (manager (& ModuleManager) module (* Module) &return bool)
(unless (fileIsMoreRecentlyModified "Dependencies/SDL/VisualC/x64/Debug/SDL2.dll" "SDL2.dll")
(return true))
(Log "SDL: Copying DLLs to working directory\n")
(unless (copyBinaryFileTo "Dependencies/SDL/VisualC/x64/Debug/SDL2.dll" "SDL2.dll")
(Log "SDL: Failed to copy SDL2 dlls to working directory\n")
(return false))
(return true))
(add-compile-time-hook-module pre-build copy-sdl2-dlls)))