diff --git a/.gitignore b/.gitignore index dbb0c3b..7be4801 100644 --- a/.gitignore +++ b/.gitignore @@ -1,34 +1,4 @@ -# Prerequisites -*.d - -# Compiled Object files -*.slo -*.lo -*.o -*.obj - -# Precompiled Headers -*.gch -*.pch - -# Compiled Dynamic libraries -*.so -*.dylib -*.dll - -# Fortran module files -*.mod -*.smod - -# Compiled Static libraries -*.lai -*.la -*.a -*.lib - -# Executables -*.exe -*.out -*.app +.DS_Store +build/ +/.gradle /.vscode -/build diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..ae3ab7a --- /dev/null +++ b/.gitmodules @@ -0,0 +1,12 @@ +[submodule "SDL"] + path = SDL + url = https://github.com/libsdl-org/SDL +[submodule "SDL_ttf"] + path = SDL_ttf + url = https://github.com/libsdl-org/SDL_ttf +[submodule "SDL_mixer"] + path = SDL_mixer + url = https://github.com/libsdl-org/SDL_mixer +[submodule "SDL_image"] + path = SDL_image + url = https://github.com/libsdl-org/SDL_image diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..7ff9d18 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,196 @@ +cmake_minimum_required(VERSION 3.16) + +# set the output directory for built objects. +# This makes sure that the dynamic library goes into the build directory automatically. +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/$") +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/$") + +# prevent installing to system directories. +set(CMAKE_INSTALL_PREFIX "${CMAKE_BINARY_DIR}" CACHE INTERNAL "") + +# Declare the project +project(sdl-min) + +if ((APPLE AND NOT CMAKE_SYSTEM_NAME MATCHES "Darwin") OR EMSCRIPTEN) + set(BUILD_SHARED_LIBS OFF CACHE INTERNAL "") # Disable shared builds on platforms where it does not make sense to use them + set(SDL_SHARED OFF) +else() + set(SDL_SHARED ON) +endif() + +if(MSVC) + if(NOT CMAKE_GENERATOR STREQUAL "Ninja") + add_definitions(/MP) # parallelize each target, unless Ninja is the generator + endif() +endif() + +# Set the name of the executable +set(EXECUTABLE_NAME ${PROJECT_NAME}) + +# Create an executable or a shared library based on the platform and add our sources to it +if (ANDROID) + # The SDL java code is hardcoded to load libmain.so on android, so we need to change EXECUTABLE_NAME + set(EXECUTABLE_NAME main) + add_library(${EXECUTABLE_NAME} SHARED) +else() + add_executable(${EXECUTABLE_NAME}) +endif() + +# Add your sources to the target +target_sources(${EXECUTABLE_NAME} +PRIVATE + src/main.cpp + src/assets/iosLaunchScreen.storyboard +) +# What is iosLaunchScreen.storyboard? This file describes what Apple's mobile platforms +# should show the user while the application is starting up. If you don't include one, +# then you get placed in a compatibility mode that does not allow HighDPI. +# This file is referenced inside Info.plist.in, where it is marked as the launch screen file. +# It is also ignored on non-Apple platforms. + +# To get an app icon on Apple platforms, add it to your executable. +# Afterward, the image file in Info.plist.in. +if(APPLE) + target_sources("${EXECUTABLE_NAME}" PRIVATE "src/logo.png") +endif() + +# Set C++ version +target_compile_features(${EXECUTABLE_NAME} PUBLIC cxx_std_20) + +# on Web targets, we need CMake to generate a HTML webpage. +if(EMSCRIPTEN) + set(CMAKE_EXECUTABLE_SUFFIX ".html" CACHE INTERNAL "") +endif() + +# Configure SDL by calling its CMake file. +# we use EXCLUDE_FROM_ALL so that its install targets and configs don't +# pollute upwards into our configuration. +add_subdirectory(SDL EXCLUDE_FROM_ALL) + +# If you don't want SDL_ttf, then remove this section. +set(SDLTTF_VENDORED ON) # tell SDL_ttf to build its own dependencies +add_subdirectory(SDL_ttf EXCLUDE_FROM_ALL) + +# SDL_mixer (used for playing audio) +set(SDLMIXER_MIDI_NATIVE OFF) # disable formats we don't use to make the build faster and smaller. Also some of these don't work on all platforms so you'll need to do some experimentation. +set(SDLMIXER_GME OFF) +set(SDLMIXER_WAVPACK OFF) +set(SDLMIXER_MOD OFF) +set(SDLMIXER_OPUS OFF) +set(SDLMIXER_VENDORED ON) # tell SDL_mixer to build its own dependencies +add_subdirectory(SDL_mixer EXCLUDE_FROM_ALL) + +# SDL_image (used for loading various image formats) +set(SDLIMAGE_VENDORED ON) +set(SDLIMAGE_AVIF OFF) # disable formats we don't use to make the build faster and smaller. +set(SDLIMAGE_BMP OFF) +set(SDLIMAGE_JPEG OFF) +set(SDLIMAGE_WEBP OFF) +add_subdirectory(SDL_image EXCLUDE_FROM_ALL) + +# Link SDL to our executable. This also makes its include directory available to us. +target_link_libraries(${EXECUTABLE_NAME} PUBLIC + SDL3_ttf::SDL3_ttf # remove if you are not using SDL_ttf + SDL3_mixer::SDL3_mixer # remove if you are not using SDL_mixer + SDL3_image::SDL3_image # remove if you are not using SDL_image + SDL3::SDL3 # If using satelite libraries, SDL must be the last item in the list. +) + +# SDL_Image bug: https://github.com/libsdl-org/SDL_image/issues/506 +if (APPLE AND NOT BUILD_SHARED_LIBS) + find_library(IO_LIB ImageIO REQUIRED) + find_library(CS_LIB CoreServices REQUIRED) + find_library(CT_LIB CoreText REQUIRED) + find_library(CG_LIB CoreGraphics REQUIRED) + find_library(CF_LIB CoreFoundation REQUIRED) + target_link_libraries(${EXECUTABLE_NAME} PUBLIC ${CF_LIB} ${CT_LIB} ${IO_LIB} ${CS_LIB} ${CG_LIB}) +endif() +target_compile_definitions(${EXECUTABLE_NAME} PUBLIC SDL_MAIN_USE_CALLBACKS) + +# Dealing with assets +# We have some non-code resources that our application needs in order to work. How we deal with those differs per platform. +if (APPLE) + # on Apple targets, the application bundle has a "resources" subfolder where we can place our assets. + # SDL_GetBasePath() gives us easy access to that location. + set(input_root "${CMAKE_CURRENT_LIST_DIR}/src") + macro(add_resource FILE) + file(RELATIVE_PATH relpath "${input_root}" "${FILE}") + get_filename_component(relpath "${relpath}" DIRECTORY) + target_sources(${EXECUTABLE_NAME} PRIVATE ${FILE}) + set_property(SOURCE ${FILE} PROPERTY MACOSX_PACKAGE_LOCATION "Resources/${relpath}") + endmacro() + add_resource("${CMAKE_CURRENT_LIST_DIR}/src/Inter-VariableFont.ttf") + add_resource("${CMAKE_CURRENT_LIST_DIR}/src/the_entertainer.ogg") + add_resource("${CMAKE_CURRENT_LIST_DIR}/src/gs_tiger.svg") +elseif(EMSCRIPTEN) + # on the web, we have to put the files inside of the webassembly + # somewhat unintuitively, this is done via a linker argument. + target_link_libraries(${EXECUTABLE_NAME} PRIVATE + "--preload-file \"${CMAKE_CURRENT_LIST_DIR}/src/Inter-VariableFont.ttf@/\"" + "--preload-file \"${CMAKE_CURRENT_LIST_DIR}/src/the_entertainer.ogg@/\"" + "--preload-file \"${CMAKE_CURRENT_LIST_DIR}/src/gs_tiger.svg@/\"" + ) +else() + if (ANDROID) + file(MAKE_DIRECTORY "${MOBILE_ASSETS_DIR}") # we need to create the project Assets dir otherwise CMake won't copy our assets. + endif() + + + macro(copy_helper filename) + if (ANDROID) + # MOBILE_ASSETS_DIR is set in the gradle file via the cmake command line and points to the Android Studio Assets folder. + # when we copy assets there, the Android build pipeline knows to add them to the apk. + set(outname "${MOBILE_ASSETS_DIR}/${filename}") + else() + # for platforms that do not have a good packaging format, all we can do is copy the assets to the process working directory. + set(outname "${CMAKE_BINARY_DIR}/$/${filename}") + endif() + add_custom_command(POST_BUILD + TARGET "${EXECUTABLE_NAME}" + COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_CURRENT_LIST_DIR}/src/${filename}" "${outname}" + DEPENDS "${filename}" + ) + endmacro() + copy_helper("assets/Inter-VariableFont.ttf") + copy_helper("assets/the_entertainer.ogg") + copy_helper("assets/gs_tiger.svg") +endif() + +# set some extra configs for each platform +set_target_properties(${EXECUTABLE_NAME} PROPERTIES + # On macOS, make a proper .app bundle instead of a bare executable + MACOSX_BUNDLE TRUE + # Set the Info.plist file for Apple Mobile platforms. Without this file, your app + # will not launch. + MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/src/Info.plist.in" + + # in Xcode, create a Scheme in the schemes dropdown for the app. + XCODE_GENERATE_SCHEME TRUE + # Identification for Xcode + XCODE_ATTRIBUTE_BUNDLE_IDENTIFIER "com.ravbug.sdl3-sample" + XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER "com.ravbug.sdl3-sample" + XCODE_ATTRIBUTE_CURRENTYEAR "${CURRENTYEAR}" +) + +# on Visual Studio, set our app as the default project +set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT "${EXECUTABLE_NAME}") + +# On macOS Platforms, ensure that the bundle is valid for distribution by calling fixup_bundle. +# note that fixup_bundle does not work on iOS, so you will want to use static libraries +# or manually copy dylibs and set rpaths +if(CMAKE_SYSTEM_NAME MATCHES "Darwin") + # tell Install about the target, otherwise fixup won't know about the transitive dependencies + install(TARGETS ${EXECUTABLE_NAME} + BUNDLE DESTINATION ./install COMPONENT Runtime + RUNTIME DESTINATION ./install/bin COMPONENT Runtime + ) + + set(DEP_DIR "${CMAKE_BINARY_DIR}") # where to look for dependencies when fixing up + INSTALL(CODE + "include(BundleUtilities) + fixup_bundle(\"$\" \"\" \"${DEP_DIR}\") + " + ) + set(CPACK_GENERATOR "DragNDrop") + include(CPack) +endif() diff --git a/SDL b/SDL new file mode 160000 index 0000000..7aba6c4 --- /dev/null +++ b/SDL @@ -0,0 +1 @@ +Subproject commit 7aba6c4c735a072ee019df94c5fda9057f6dbcd7 diff --git a/SDL_image b/SDL_image new file mode 160000 index 0000000..0ca93a0 --- /dev/null +++ b/SDL_image @@ -0,0 +1 @@ +Subproject commit 0ca93a0a4844640376b64f55f358999a90d0d0b0 diff --git a/SDL_mixer b/SDL_mixer new file mode 160000 index 0000000..ebdd9cc --- /dev/null +++ b/SDL_mixer @@ -0,0 +1 @@ +Subproject commit ebdd9cc0fe43352e33ec234f4720fd7d54a31d13 diff --git a/SDL_ttf b/SDL_ttf new file mode 160000 index 0000000..3d7b6ef --- /dev/null +++ b/SDL_ttf @@ -0,0 +1 @@ +Subproject commit 3d7b6efedd0d2c9cfc6ee0a18906550d6c98d07a diff --git a/adventura b/adventura deleted file mode 100755 index e4d48fd..0000000 Binary files a/adventura and /dev/null differ diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..25a6aaf --- /dev/null +++ b/build.gradle @@ -0,0 +1,72 @@ +def buildAsLibrary = project.hasProperty('BUILD_AS_LIBRARY'); +def buildAsApplication = !buildAsLibrary +if (buildAsApplication) { + apply plugin: 'com.android.application' +} +else { + apply plugin: 'com.android.library' +} + +android { + compileSdkVersion 34 + if (buildAsApplication) { + namespace "org.libsdl.app" + } + defaultConfig { + minSdkVersion 21 + targetSdkVersion 34 + versionCode 1 + versionName "1.0" + externalNativeBuild { + cmake { + arguments "-DANDROID_APP_PLATFORM=android-16", "-DANDROID_STL=c++_static", "-DCMAKE_VERBOSE_MAKEFILE=ON", "-DMOBILE_ASSETS_DIR=${System.getProperty("user.dir")}/app/src/main/assets" + // abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64' + // abiFilters 'arm64-v8a' + } + } + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } + applicationVariants.all { variant -> + tasks["merge${variant.name.capitalize()}Assets"] + .dependsOn("externalNativeBuild${variant.name.capitalize()}") + } + if (!project.hasProperty('EXCLUDE_NATIVE_LIBS')) { + sourceSets.main { + jniLibs.srcDir 'libs' + } + externalNativeBuild { + // ndkBuild { + // path 'jni/Android.mk' + // } + cmake { + path "../../../CMakeLists.txt" + version "3.22.1" + } + } + + } + lintOptions { + abortOnError false + } + + if (buildAsLibrary) { + libraryVariants.all { variant -> + variant.outputs.each { output -> + def outputFile = output.outputFile + if (outputFile != null && outputFile.name.endsWith(".aar")) { + def fileName = "org.libsdl.app.aar"; + output.outputFile = new File(outputFile.parent, fileName); + } + } + } + } +} + +dependencies { + implementation fileTree(include: ['*.jar'], dir: 'libs') +} diff --git a/config/README.md b/config/README.md new file mode 100644 index 0000000..ec7d861 --- /dev/null +++ b/config/README.md @@ -0,0 +1,18 @@ +# How to use these scripts + +For the most part, just run the scripts in this directory from this directory. + +## Notes: +- visionOS + - Requires CMake 3.28 or newer +- Web + 1. Install emsdk somewhere. + 2. On Windows hosts, run `config-web-win.bat` via the `emcmdprompt.bat` cmd in the emsdk root directory + 3. On Unix hosts, first run `source emsdk_env.sh` (found in the emsdk root directory), then run `config-web-unix.sh` + 4. After the build completes, use `python3 -m http.server` in the build directory to make the page accessible. +- Android + - There is no easy CMake config for this platform. Instead, follow the steps as outlined below. See the android GitHub action for more details. + 1. Install Android Studio + NDK from https://developer.android.com/studio + 2. Copy `build.gradle` to `SDL/android-project/app/`. If you look inside `build.gradle`, you'll see `"../../../CMakeLists.txt"` as the path to the CMakeLists file. This points to the CMakeLists in this repo's root directory after it is copied. + 3. `cd SDL/android-project/` + 4. `./gradlew assembleDebug`. You'll get an apk which you can then install onto a device. diff --git a/config/config-generic.sh b/config/config-generic.sh new file mode 100755 index 0000000..fb24e58 --- /dev/null +++ b/config/config-generic.sh @@ -0,0 +1,4 @@ +#!/bin/bash +cd "${0%/*}"/ + +cmake -B "../build/`uname`" -S .. \ No newline at end of file diff --git a/config/config-ios-xcode.sh b/config/config-ios-xcode.sh new file mode 100755 index 0000000..9a3f30d --- /dev/null +++ b/config/config-ios-xcode.sh @@ -0,0 +1,4 @@ +#!/bin/bash +cd "${0%/*}" + +cmake -G "Xcode" -DCMAKE_SYSTEM_NAME="iOS" -B ../build/ios -S .. diff --git a/config/config-mac-xcode.sh b/config/config-mac-xcode.sh new file mode 100755 index 0000000..7608429 --- /dev/null +++ b/config/config-mac-xcode.sh @@ -0,0 +1,4 @@ +#!/bin/bash +cd "${0%/*}" + +cmake -G "Xcode" -B ../build/mac -S .. \ No newline at end of file diff --git a/config/config-tvos-xcode.sh b/config/config-tvos-xcode.sh new file mode 100755 index 0000000..bab9c34 --- /dev/null +++ b/config/config-tvos-xcode.sh @@ -0,0 +1,4 @@ +#!/bin/bash +cd "${0%/*}" + +cmake -G "Xcode" -DCMAKE_SYSTEM_NAME="tvOS" -B ../build/tvos -S .. diff --git a/config/config-visionos-xcode.sh b/config/config-visionos-xcode.sh new file mode 100644 index 0000000..2b174c2 --- /dev/null +++ b/config/config-visionos-xcode.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +# note: requires CMake 3.28 or newer + +cd "${0%/*}" + +cmake -G "Xcode" -DCMAKE_SYSTEM_NAME="visionOS" -S .. -B ../build/visionOS diff --git a/config/config-web-unix.sh b/config/config-web-unix.sh new file mode 100755 index 0000000..ce6e5c5 --- /dev/null +++ b/config/config-web-unix.sh @@ -0,0 +1,4 @@ +#!/bin/bash +cd "${0%/*}" + +emcmake cmake -S .. -B ../build/web \ No newline at end of file diff --git a/config/config-web-win.bat b/config/config-web-win.bat new file mode 100644 index 0000000..7f3cc2d --- /dev/null +++ b/config/config-web-win.bat @@ -0,0 +1,3 @@ +@echo OFF + +emcmake cmake -S .. -B ..\build\web \ No newline at end of file diff --git a/config/config-win.bat b/config/config-win.bat new file mode 100644 index 0000000..08b3623 --- /dev/null +++ b/config/config-win.bat @@ -0,0 +1,3 @@ +@echo OFF + +cmake -S .. -B ..\build\win \ No newline at end of file diff --git a/src/assets/Info.plist.in b/src/assets/Info.plist.in new file mode 100644 index 0000000..4478aa4 --- /dev/null +++ b/src/assets/Info.plist.in @@ -0,0 +1,45 @@ + + + + + NSHighResolutionCapable + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + UILaunchStoryboardName + iosLaunchScreen.storyboard + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIconFile + + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + LSMinimumSystemVersion + $(MACOSX_DEPLOYMENT_TARGET) + NSHumanReadableCopyright + Copyright © $(CURRENTYEAR) Ravbug. All rights reserved. + NSSupportsAutomaticTermination + + NSSupportsSuddenTermination + + CFBundleIconFile + logo.png + UIRequiresFullScreen + + UISupportedInterfaceOrientations + + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/src/assets/Inter-VariableFont.ttf b/src/assets/Inter-VariableFont.ttf new file mode 100644 index 0000000..32a7999 Binary files /dev/null and b/src/assets/Inter-VariableFont.ttf differ diff --git a/src/assets/attributions.txt b/src/assets/attributions.txt new file mode 100644 index 0000000..2fc7fde --- /dev/null +++ b/src/assets/attributions.txt @@ -0,0 +1,3 @@ +"The Entertainer" Kevin MacLeod (incompetech.com) +Licensed under Creative Commons: By Attribution 4.0 License +http://creativecommons.org/licenses/by/4.0/ \ No newline at end of file diff --git a/src/assets/gs_tiger.svg b/src/assets/gs_tiger.svg new file mode 100644 index 0000000..679edec --- /dev/null +++ b/src/assets/gs_tiger.svg @@ -0,0 +1,725 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/assets/iosLaunchScreen.storyboard b/src/assets/iosLaunchScreen.storyboard new file mode 100644 index 0000000..87b510f --- /dev/null +++ b/src/assets/iosLaunchScreen.storyboard @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/src/assets/logo.png b/src/assets/logo.png new file mode 100644 index 0000000..b393eee Binary files /dev/null and b/src/assets/logo.png differ diff --git a/src/assets/main.cpp b/src/assets/main.cpp new file mode 100644 index 0000000..f9f65e6 --- /dev/null +++ b/src/assets/main.cpp @@ -0,0 +1,195 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +constexpr uint32_t windowStartWidth = 400; +constexpr uint32_t windowStartHeight = 400; + +struct AppContext { + SDL_Window* window; + SDL_Renderer* renderer; + SDL_Texture* messageTex, *imageTex; + SDL_FRect messageDest; + SDL_AudioDeviceID audioDevice; + Mix_Music* music; + SDL_AppResult app_quit = SDL_APP_CONTINUE; +}; + +SDL_AppResult SDL_Fail(){ + SDL_LogError(SDL_LOG_CATEGORY_CUSTOM, "Error %s", SDL_GetError()); + return SDL_APP_FAILURE; +} + +SDL_AppResult SDL_AppInit(void** appstate, int argc, char* argv[]) { + // init the library, here we make a window so we only need the Video capabilities. + if (not SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO)){ + return SDL_Fail(); + } + + // init TTF + if (not TTF_Init()) { + return SDL_Fail(); + } + + // create a window + + SDL_Window* window = SDL_CreateWindow("SDL Minimal Sample", windowStartWidth, windowStartHeight, SDL_WINDOW_RESIZABLE | SDL_WINDOW_HIGH_PIXEL_DENSITY); + if (not window){ + return SDL_Fail(); + } + + // create a renderer + SDL_Renderer* renderer = SDL_CreateRenderer(window, NULL); + if (not renderer){ + return SDL_Fail(); + } + + // load the font +#if __ANDROID__ + std::filesystem::path basePath = ""; // on Android we do not want to use basepath. Instead, assets are available at the root directory. +#else + auto basePathPtr = SDL_GetBasePath(); + if (not basePathPtr){ + return SDL_Fail(); + } + const std::filesystem::path basePath = basePathPtr; +#endif + + const auto fontPath = basePath / "Inter-VariableFont.ttf"; + TTF_Font* font = TTF_OpenFont(fontPath.string().c_str(), 36); + if (not font) { + return SDL_Fail(); + } + + // render the font to a surface + const std::string_view text = "Hello SDL!"; + SDL_Surface* surfaceMessage = TTF_RenderText_Solid(font, text.data(), text.length(), { 255,255,255 }); + + // make a texture from the surface + SDL_Texture* messageTex = SDL_CreateTextureFromSurface(renderer, surfaceMessage); + + // we no longer need the font or the surface, so we can destroy those now. + TTF_CloseFont(font); + SDL_DestroySurface(surfaceMessage); + + // load the SVG + auto svg_surface = IMG_Load((basePath / "gs_tiger.svg").string().c_str()); + SDL_Texture* tex = SDL_CreateTextureFromSurface(renderer, svg_surface); + SDL_DestroySurface(svg_surface); + + + // get the on-screen dimensions of the text. this is necessary for rendering it + auto messageTexProps = SDL_GetTextureProperties(messageTex); + SDL_FRect text_rect{ + .x = 0, + .y = 0, + .w = float(SDL_GetNumberProperty(messageTexProps, SDL_PROP_TEXTURE_WIDTH_NUMBER, 0)), + .h = float(SDL_GetNumberProperty(messageTexProps, SDL_PROP_TEXTURE_HEIGHT_NUMBER, 0)) + }; + + // init SDL Mixer + auto audioDevice = SDL_OpenAudioDevice(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, NULL); + if (not audioDevice) { + return SDL_Fail(); + } + if (not Mix_OpenAudio(audioDevice, NULL)) { + return SDL_Fail(); + } + + // load the music + auto musicPath = basePath / "the_entertainer.ogg"; + auto music = Mix_LoadMUS(musicPath.string().c_str()); + if (not music) { + return SDL_Fail(); + } + + // play the music (does not loop) + Mix_PlayMusic(music, 0); + + // print some information about the window + SDL_ShowWindow(window); + { + int width, height, bbwidth, bbheight; + SDL_GetWindowSize(window, &width, &height); + SDL_GetWindowSizeInPixels(window, &bbwidth, &bbheight); + SDL_Log("Window size: %ix%i", width, height); + SDL_Log("Backbuffer size: %ix%i", bbwidth, bbheight); + if (width != bbwidth){ + SDL_Log("This is a highdpi environment."); + } + } + + // set up the application data + *appstate = new AppContext{ + .window = window, + .renderer = renderer, + .messageTex = messageTex, + .imageTex = tex, + .messageDest = text_rect, + .audioDevice = audioDevice, + .music = music, + }; + + SDL_SetRenderVSync(renderer, -1); // enable vysnc + + SDL_Log("Application started successfully!"); + + return SDL_APP_CONTINUE; +} + +SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event* event) { + auto* app = (AppContext*)appstate; + + if (event->type == SDL_EVENT_QUIT) { + app->app_quit = SDL_APP_SUCCESS; + } + + return SDL_APP_CONTINUE; +} + +SDL_AppResult SDL_AppIterate(void *appstate) { + auto* app = (AppContext*)appstate; + + // draw a color + auto time = SDL_GetTicks() / 1000.f; + auto red = (std::sin(time) + 1) / 2.0 * 255; + auto green = (std::sin(time / 2) + 1) / 2.0 * 255; + auto blue = (std::sin(time) * 2 + 1) / 2.0 * 255; + + SDL_SetRenderDrawColor(app->renderer, red, green, blue, SDL_ALPHA_OPAQUE); + SDL_RenderClear(app->renderer); + + // Renderer uses the painter's algorithm to make the text appear above the image, we must render the image first. + SDL_RenderTexture(app->renderer, app->imageTex, NULL, NULL); + SDL_RenderTexture(app->renderer, app->messageTex, NULL, &app->messageDest); + + SDL_RenderPresent(app->renderer); + + return app->app_quit; +} + +void SDL_AppQuit(void* appstate, SDL_AppResult result) { + auto* app = (AppContext*)appstate; + if (app) { + SDL_DestroyRenderer(app->renderer); + SDL_DestroyWindow(app->window); + + Mix_FadeOutMusic(1000); // prevent the music from abruptly ending. + Mix_FreeMusic(app->music); // this call blocks until the music has finished fading + Mix_CloseAudio(); + SDL_CloseAudioDevice(app->audioDevice); + + delete app; + } + TTF_Quit(); + Mix_Quit(); + + SDL_Log("Application quit successfully!"); + SDL_Quit(); +} diff --git a/src/assets/the_entertainer.ogg b/src/assets/the_entertainer.ogg new file mode 100644 index 0000000..e4203ac Binary files /dev/null and b/src/assets/the_entertainer.ogg differ