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.

368 lines
18 KiB

(import "Dependencies.cake" "CHelpers.cake" "ComptimeHelpers.cake"
"DynamicArray.cake" ;; For dynstring text widgets
&defs-only "Licenses.cake")
(c-import "imgui.h")
(register-module-license "dear imgui" s-imgui-license-string)
(register-module-license "Ubuntu font" s-ubuntu-font-license-string)
;; The main interface into imgui.
(defmacro imgui-call (function symbol &rest &optional arguments any)
(if arguments
(tokenize-push output
(call (in ImGui (token-splice function)) (token-splice-rest arguments tokens)))
(tokenize-push output
(call (in ImGui (token-splice function)))))
(return true))
(defun imgui-do-tooltip (text (addr (const char)) shortcut (addr (const char)))
(when (imgui-call IsItemHovered)
(imgui-call BeginTooltip)
(imgui-call PushTextWrapPos (* (imgui-call GetFontSize) 35.0f))
(imgui-call TextWrapped "%s" text)
(when shortcut
(imgui-call TextDisabled "%s" shortcut))
(imgui-call EndTooltip)))
(defun imgui-first-time-window-size (x float y float width float height float)
(var main-viewport (addr (const ImGuiViewport)) (imgui-call GetMainViewport))
(var imgui-style (ref ImGuiStyle) (imgui-call GetStyle))
(var menu-bar-approx-offset float (+ (* 2 (field imgui-style FramePadding y))
(imgui-call GetFontSize)))
(imgui-call SetNextWindowPos (ImVec2 (+ (path main-viewport > WorkPos . x)
(* x (path main-viewport > WorkSize . x)))
(+ (path main-viewport > WorkPos . y)
(* y (- (path main-viewport > WorkSize . y)
(imgui-call SetNextWindowSize (ImVec2 (* (path main-viewport > WorkSize . x) width)
(* (- (path main-viewport > WorkSize . y)
(defun-local imgui-input-text-dynstring-resize (data (addr ImGuiInputTextCallbackData)
&return int)
(when (= (path data > EventFlag) ImGuiInputTextFlags_CallbackResize)
(var-cast-to dynstring-out (addr (addr char)) (path data > UserData))
;; Minimize reallocations by making sure we have a decent amount of capacity
(var resize-at-capacity (unsigned int)
;; 75% of the current capacity
(+ (* (/ (dynarray-capacity (deref dynstring-out)) 4) 3)))
;; This factor should always be larger than the resize-at-capacity percentage
(var resize-factor (unsigned int) 2)
;; Use the BufSize rather than the dynarray-length to ensure if the buffer has greatly
;; increased, we have room for it. This happens when the user e.g. copy pastes a block of text
;; rather than types a single character.
(when (>= (path data > BufSize) resize-at-capacity)
(var new-capacity (unsigned int) (* resize-factor (dynarray-capacity (deref dynstring-out))))
;; Finally, if the BufSize is much much larger than even our resize, just use it to set our new size
(when (< new-capacity (path data > BufSize))
(set new-capacity (* 2 (path data > BufSize))))
(dynarray-set-capacity (deref dynstring-out)
(dynarray-set-length (deref dynstring-out) (path data > BufSize))
(set (path data > Buf) (deref dynstring-out)))
(return 0))
(defun imgui-input-text-multiline-to-dynstring (label (addr (const char))
dynstring-out (addr (addr char))
width float
height float
flags int ;; ImGuiInputTextFlags
&return bool)
;; ImGui can't work with a completely null buffer
(unless (deref dynstring-out)
(dynstring-make-empty-string dynstring-out 64))
(return (imgui-call InputTextMultiline label (deref dynstring-out)
;; Note we use the full length because ImGui expects space for null terminator
(dynarray-length (deref dynstring-out))
(ImVec2 width height)
(bit-or flags ImGuiInputTextFlags_CallbackResize)
imgui-input-text-dynstring-resize (type-cast dynstring-out (addr void)))))
;; When you just don't care about the details and want a ImGui window
(defmacro make-imgui-sdl-gl3-application (entry-point-name symbol
window-name string
initialization array
once-per-frame array
shutdown array)
;; This is somewhat unusual. Make unique font data variable names due to "var" always being a
;; global declaration to Cakelisp. The data will still only be bundled once.
;; TODO think of a way to clean this up. DataBundle should handle this better for us.
(var start-font-data-name Token (deref entry-point-name))
(token-contents-snprintf start-font-data-name "start-font-data-%s"
(call-on c_str (field start-font-data-name contents)))
(var end-font-data-name Token (deref entry-point-name))
(token-contents-snprintf end-font-data-name "end-font-data-%s"
(call-on c_str (field end-font-data-name contents)))
(tokenize-push output
(import "DataBundle.cake")
(bundle-file (token-splice-addr start-font-data-name) (token-splice-addr end-font-data-name)
(unsigned char) "data/Fonts/Ubuntu-R.ttf")
(defun (token-splice entry-point-name) (&return int)
;; We are handling DPI scaling, so don't let Windows scale us again
;; Must do this before choosing the GL version
;; Not actually used: ImGuiSDLOpenGL_SetAttributes override these for SDL_GL_CreateContext
(var display-bounds SDL_Rect (array 0))
;; Primary display
(SDL_GetDisplayBounds 0 (addr display-bounds))
;; Define the window size as a factor of the display size so that it should always fit
(var window-display-ratio float 0.75f)
(var window (addr SDL_Window) null)
(set window
(SDL_CreateWindow (token-splice window-name)
(* window-display-ratio (field display-bounds w))
(* window-display-ratio (field display-bounds h))
(unless window
(return 1))
(var glsl-version (addr (const char)) null)
(ImGuiSDLOpenGL_SetAttributes (addr glsl-version))
(var gl-context SDL_GLContext (SDL_GL_CreateContext window))
(SDL_GL_MakeCurrent window gl-context)
(SDL_GL_SetSwapInterval 1) ;; Enable vsync
(unless (ImGuiSDLOpenGL_InitializeGLLoader)
(fprintf stderr "Failed ImGuiSDLOpenGL_InitializeGLLoader\n")
(return 1))
(imgui-call CreateContext)
(scope ;; Enable docking
(var imgui-io (ref ImGuiIO) (imgui-call GetIO))
(set (field imgui-io ConfigFlags) (bit-or (field imgui-io ConfigFlags)
(scope ;; Enable keyboard navigation
(var imgui-io (ref ImGuiIO) (imgui-call GetIO))
(set (field imgui-io ConfigFlags) (bit-or (field imgui-io ConfigFlags)
;; DPI scaling
(var g-dpi-scale float 1.f)
;; Somewhat arbitrary numbers here which seemed to make sense on my 4k screen
(when (> (field display-bounds h) 1440)
(set g-dpi-scale 1.6f))
(var style (ref ImGuiStyle) (imgui-call GetStyle))
(call-on ScaleAllSizes style g-dpi-scale))
(scope ;; Font
(var imgui-io (ref ImGuiIO) (imgui-call GetIO))
(var g-font-size-pixels int 16)
(var font-config ImFontConfig)
;; The data is bundled in the executable, so don't try to delete it
(set (field font-config FontDataOwnedByAtlas) false)
(var ubuntu-mono-font (addr ImFont)
(call-on-ptr AddFontFromMemoryTTF (field imgui-io Fonts)
(token-splice-addr start-font-data-name)
(- (token-splice-addr end-font-data-name)
(token-splice-addr start-font-data-name))
(* g-font-size-pixels g-dpi-scale)
(addr font-config)))
;; imgui-io.Fonts->GetTexDataAsRGBA32();
(call-on-ptr Build (field imgui-io Fonts)))
;; (imgui-call StyleColorsDark)
;; Note: Passing in null for the context will break Viewports branch of ImGui
(unless (ImGui_ImplSDL2_InitForOpenGL window gl-context)
(fprintf stderr "Failed ImGui_ImplSDL2_InitForOpenGL\n")
(return 1))
(ImGui_ImplOpenGL3_Init glsl-version)
(token-splice initialization)
(var exit-reason (addr (const char)) null)
(while (not exit-reason)
(var event SDL_Event)
(while (SDL_PollEvent (addr event))
(ImGui_ImplSDL2_ProcessEvent (addr event))
(when (= (field event type) SDL_QUIT)
(set exit-reason "Window event"))
(when (and (= (field event type) SDL_WINDOWEVENT)
(= (field event window event) SDL_WINDOWEVENT_CLOSE)
(= (SDL_GetWindowID window) (field event window windowID)))
(set exit-reason "Window event")))
(var currentKeyStates (addr (const Uint8)) (SDL_GetKeyboardState null))
(ImGui_ImplSDL2_NewFrame window)
(imgui-call NewFrame)
(scope ;; Probably will be handled by your game instead
(var imgui-io (ref ImGuiIO) (imgui-call GetIO))
(var clear-color ImVec4 (array 0.1f 0.1f 0.1f 1.f))
(ImGuiSDLOpenGL_ClearWindow (addr imgui-io) clear-color))
(imgui-call DockSpaceOverViewport (imgui-call GetMainViewport)
;; Allow inputs and graphics to go through the dock
(token-splice once-per-frame)
(imgui-call Render)
(ImGui_ImplOpenGL3_RenderDrawData (imgui-call GetDrawData))
(SDL_GL_SwapWindow window))
(when exit-reason
(SDL_HideWindow window)
(fprintf stderr "Exiting. Reason: %s\n" exit-reason))
(token-splice shutdown)
(imgui-call DestroyContext)
(sdl-shutdown window)
(return 0)))
(return true))
(defun imgui-set-style-red-dark ()
;; copied and modified from
(var colors (addr ImVec4) (field (imgui-call GetStyle) Colors))
(set (at ImGuiCol_Text colors) (ImVec4 0.75f 0.75f 0.75f 1.00f))
(set (at ImGuiCol_TextDisabled colors) (ImVec4 0.35f 0.35f 0.35f 1.00f))
(set (at ImGuiCol_WindowBg colors) (ImVec4 0.00f 0.00f 0.00f 0.94f))
(set (at ImGuiCol_ChildBg colors) (ImVec4 0.00f 0.00f 0.00f 0.00f))
(set (at ImGuiCol_PopupBg colors) (ImVec4 0.08f 0.08f 0.08f 0.94f))
(set (at ImGuiCol_Border colors) (ImVec4 0.00f 0.00f 0.00f 0.50f))
(set (at ImGuiCol_BorderShadow colors) (ImVec4 0.00f 0.00f 0.00f 0.00f))
(set (at ImGuiCol_FrameBg colors) (ImVec4 0.25f 0.25f 0.25f 0.54f))
(set (at ImGuiCol_FrameBgHovered colors) (ImVec4 0.37f 0.14f 0.14f 0.67f))
(set (at ImGuiCol_FrameBgActive colors) (ImVec4 0.39f 0.20f 0.20f 0.67f))
(set (at ImGuiCol_TitleBg colors) (ImVec4 0.04f 0.04f 0.04f 1.00f))
(set (at ImGuiCol_TitleBgActive colors) (ImVec4 0.48f 0.16f 0.16f 1.00f))
(set (at ImGuiCol_TitleBgCollapsed colors) (ImVec4 0.48f 0.16f 0.16f 1.00f))
(set (at ImGuiCol_MenuBarBg colors) (ImVec4 0.14f 0.14f 0.14f 1.00f))
(set (at ImGuiCol_ScrollbarBg colors) (ImVec4 0.02f 0.02f 0.02f 0.53f))
(set (at ImGuiCol_ScrollbarGrab colors) (ImVec4 0.31f 0.31f 0.31f 1.00f))
(set (at ImGuiCol_ScrollbarGrabHovered colors) (ImVec4 0.41f 0.41f 0.41f 1.00f))
(set (at ImGuiCol_ScrollbarGrabActive colors) (ImVec4 0.51f 0.51f 0.51f 1.00f))
(set (at ImGuiCol_CheckMark colors) (ImVec4 0.56f 0.10f 0.10f 1.00f))
(set (at ImGuiCol_SliderGrab colors) (ImVec4 1.00f 0.19f 0.19f 0.40f))
(set (at ImGuiCol_SliderGrabActive colors) (ImVec4 0.89f 0.00f 0.19f 1.00f))
(set (at ImGuiCol_Button colors) (ImVec4 1.00f 0.19f 0.19f 0.40f))
(set (at ImGuiCol_ButtonHovered colors) (ImVec4 0.80f 0.17f 0.00f 1.00f))
(set (at ImGuiCol_ButtonActive colors) (ImVec4 0.89f 0.00f 0.19f 1.00f))
(set (at ImGuiCol_Header colors) (ImVec4 0.33f 0.35f 0.36f 0.53f))
(set (at ImGuiCol_HeaderHovered colors) (ImVec4 0.76f 0.28f 0.44f 0.67f))
(set (at ImGuiCol_HeaderActive colors) (ImVec4 0.47f 0.47f 0.47f 0.67f))
(set (at ImGuiCol_Separator colors) (ImVec4 0.32f 0.32f 0.32f 1.00f))
(set (at ImGuiCol_SeparatorHovered colors) (ImVec4 0.32f 0.32f 0.32f 1.00f))
(set (at ImGuiCol_SeparatorActive colors) (ImVec4 0.32f 0.32f 0.32f 1.00f))
(set (at ImGuiCol_ResizeGrip colors) (ImVec4 1.00f 1.00f 1.00f 0.85f))
(set (at ImGuiCol_ResizeGripHovered colors) (ImVec4 1.00f 1.00f 1.00f 0.60f))
(set (at ImGuiCol_ResizeGripActive colors) (ImVec4 1.00f 1.00f 1.00f 0.90f))
(set (at ImGuiCol_Tab colors) (ImVec4 0.07f 0.07f 0.07f 0.51f))
(set (at ImGuiCol_TabHovered colors) (ImVec4 0.86f 0.23f 0.43f 0.67f))
(set (at ImGuiCol_TabActive colors) (ImVec4 0.19f 0.19f 0.19f 0.57f))
(set (at ImGuiCol_TabUnfocused colors) (ImVec4 0.05f 0.05f 0.05f 0.90f))
(set (at ImGuiCol_TabUnfocusedActive colors) (ImVec4 0.13f 0.13f 0.13f 0.74f))
(set (at ImGuiCol_DockingPreview colors) (ImVec4 0.47f 0.47f 0.47f 0.47f))
(set (at ImGuiCol_DockingEmptyBg colors) (ImVec4 0.20f 0.20f 0.20f 1.00f))
(set (at ImGuiCol_PlotLines colors) (ImVec4 0.61f 0.61f 0.61f 1.00f))
(set (at ImGuiCol_PlotLinesHovered colors) (ImVec4 1.00f 0.43f 0.35f 1.00f))
(set (at ImGuiCol_PlotHistogram colors) (ImVec4 0.90f 0.70f 0.00f 1.00f))
(set (at ImGuiCol_PlotHistogramHovered colors) (ImVec4 1.00f 0.60f 0.00f 1.00f))
(set (at ImGuiCol_TableHeaderBg colors) (ImVec4 0.19f 0.19f 0.20f 1.00f))
(set (at ImGuiCol_TableBorderStrong colors) (ImVec4 0.31f 0.31f 0.35f 1.00f))
(set (at ImGuiCol_TableBorderLight colors) (ImVec4 0.23f 0.23f 0.25f 1.00f))
(set (at ImGuiCol_TableRowBg colors) (ImVec4 0.00f 0.00f 0.00f 0.00f))
(set (at ImGuiCol_TableRowBgAlt colors) (ImVec4 1.00f 1.00f 1.00f 0.07f))
(set (at ImGuiCol_TextSelectedBg colors) (ImVec4 0.26f 0.59f 0.98f 0.35f))
(set (at ImGuiCol_DragDropTarget colors) (ImVec4 1.00f 1.00f 0.00f 0.90f))
(set (at ImGuiCol_NavHighlight colors) (ImVec4 0.26f 0.59f 0.98f 1.00f))
(set (at ImGuiCol_NavWindowingHighlight colors) (ImVec4 1.00f 1.00f 1.00f 0.70f))
(set (at ImGuiCol_NavWindowingDimBg colors) (ImVec4 0.80f 0.80f 0.80f 0.20f))
(set (at ImGuiCol_ModalWindowDimBg colors) (ImVec4 0.80f 0.80f 0.80f 0.35f)))
;; Building
(add-dependency-git-submodule clone-imgui ""
;; TODO This is a hack: add build output dir as search directory. Should be built in?
(defun-comptime add-search-build-directory (manager (ref ModuleManager) module (addr Module)
&return bool)
(call-on push_back (path module > cSearchDirectories)
(call-on c_str (field manager buildOutputDir)))
(return true))
;; Different loaders are available. Loader selection could be done via macro to allow that flexibility
(import "OpenGL.cake")
(import "SDL.cake")
(c-import "imgui.h"
"imgui_impl_sdl.h" ;; Platform (inputs etc.)
"imgui_impl_opengl3.h") ;; Rendering
;; From ImGuiSDLOpenGL.cpp
(declare-extern-function ImGuiSDLOpenGL_SetAttributes (glsl-version (addr (addr (const char)))))
(declare-extern-function ImGuiSDLOpenGL_InitializeGLLoader (&return bool))
(declare-extern-function ImGuiSDLOpenGL_ClearWindow (io (addr ImGuiIO) clear-color ImVec4))
(add-compile-time-hook-module pre-build add-search-build-directory)
(add-library-dependency "GL" "dl"))
(add-static-link-objects "opengl32.lib"))))
(add-cpp-build-dependency "imgui.cpp"
(add-cpp-build-dependency "imgui_impl_sdl.cpp" "ImGuiSDLOpenGL.cpp" "imgui_impl_opengl3.cpp")
;; Testing
(c-import "<stdio.h>")
(make-imgui-sdl-gl3-application ;; If you want your own entry point, check this macro's body
"GameLib ImGui test"
(scope (fprintf stderr "Initializing\n")) ;; Initialization
(scope ;; Once per frame
(imgui-call ShowDemoWindow))
(scope (fprintf stderr "Shut down\n"))))) ;; Shutdown