Squashed 'external/SDL/' content from commit f317581c91
git-subtree-dir: external/SDL git-subtree-split: f317581c91961ee628446886dd4df87e9c16ac79
This commit is contained in:
441
examples/CMakeLists.txt
Normal file
441
examples/CMakeLists.txt
Normal file
@@ -0,0 +1,441 @@
|
||||
#
|
||||
# CMake script for building the SDL examples
|
||||
#
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/../cmake")
|
||||
|
||||
include(CheckIncludeFile)
|
||||
include(CheckStructHasMember)
|
||||
include(CMakePushCheckState)
|
||||
include(sdlcompilers)
|
||||
|
||||
if(SDL_EXAMPLES_LINK_SHARED)
|
||||
set(sdl_name_component SDL3-shared)
|
||||
else()
|
||||
set(sdl_name_component SDL3-static)
|
||||
endif()
|
||||
set(HAVE_EXAMPLES_LINK_SHARED "${SDL_EXAMPLES_LINK_SHARED}" PARENT_SCOPE)
|
||||
|
||||
# CMake incorrectly detects opengl32.lib being present on MSVC ARM64
|
||||
if(NOT (MSVC AND SDL_CPU_ARM64))
|
||||
# Prefer GLVND, if present
|
||||
set(OpenGL_GL_PREFERENCE GLVND)
|
||||
find_package(OpenGL)
|
||||
endif()
|
||||
|
||||
if(WINDOWS_STORE)
|
||||
cmake_minimum_required(VERSION 3.19)
|
||||
# CMP0112: Target file component generator expressions do not add target dependencies.
|
||||
cmake_policy(SET CMP0112 NEW)
|
||||
endif()
|
||||
|
||||
set(SDL_EXAMPLE_EXECUTABLES)
|
||||
|
||||
if(CMAKE_RUNTIME_OUTPUT_DIRECTORY)
|
||||
set(example_bin_dir "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}")
|
||||
if(NOT IS_ABSOLUTE "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}")
|
||||
set(example_bin_dir "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_RUNTIME_OUTPUT_DIRECTORY}")
|
||||
endif()
|
||||
else()
|
||||
set(example_bin_dir "${CMAKE_CURRENT_BINARY_DIR}")
|
||||
endif()
|
||||
if(NOT CMAKE_VERSION VERSION_LESS 3.20)
|
||||
get_property(is_multi_config GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
|
||||
set(example_bin_dir "${example_bin_dir}$<$<BOOL:${is_multi_config}>:/$<CONFIG>>")
|
||||
endif()
|
||||
|
||||
file(GLOB RESOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/../test/*.bmp ${CMAKE_CURRENT_SOURCE_DIR}/../test/*.wav ${CMAKE_CURRENT_SOURCE_DIR}/../test/*.hex)
|
||||
|
||||
set(RESOURCE_FILE_NAMES)
|
||||
set(RESOURCE_FILES_BINDIR)
|
||||
foreach(resource_file IN LISTS RESOURCE_FILES)
|
||||
get_filename_component(res_file_name ${resource_file} NAME)
|
||||
list(APPEND RESOURCE_FILE_NAMES "${res_file_name}")
|
||||
set(resource_file_bindir "${example_bin_dir}/${res_file_name}")
|
||||
add_custom_command(OUTPUT "${resource_file_bindir}"
|
||||
COMMAND "${CMAKE_COMMAND}" -E copy "${resource_file}" "${resource_file_bindir}"
|
||||
DEPENDS "${resource_file}"
|
||||
)
|
||||
list(APPEND RESOURCE_FILES_BINDIR "${resource_file_bindir}")
|
||||
endforeach()
|
||||
add_custom_target(copy-sdl-example-resources
|
||||
DEPENDS "${RESOURCE_FILES_BINDIR}"
|
||||
)
|
||||
|
||||
if(WINDOWS_STORE)
|
||||
add_library(sdl_example_main_callbacks_uwp OBJECT ${CMAKE_CURRENT_SOURCE_DIR}/../test/main.cpp)
|
||||
target_link_libraries(sdl_example_main_callbacks_uwp PRIVATE SDL3::Headers)
|
||||
target_compile_options(sdl_example_main_callbacks_uwp PRIVATE "/ZW")
|
||||
target_compile_definitions(sdl_example_main_callbacks_uwp PRIVATE "SDL_MAIN_USE_CALLBACKS")
|
||||
|
||||
set_source_files_properties(${RESOURCE_FILES} PROPERTIES VS_DEPLOYENT_LOCATION "Assets")
|
||||
endif()
|
||||
|
||||
macro(add_sdl_example_executable TARGET)
|
||||
cmake_parse_arguments(AST "BUILD_DEPENDENT" "" "SOURCES;DATAFILES" ${ARGN})
|
||||
if(AST_UNPARSED_ARGUMENTS)
|
||||
message(FATAL_ERROR "Unknown argument(s): ${AST_UNPARSED_ARGUMENTS}")
|
||||
endif()
|
||||
if(NOT AST_SOURCES)
|
||||
message(FATAL_ERROR "add_sdl_example_executable needs at least one source")
|
||||
endif()
|
||||
set(EXTRA_SOURCES "")
|
||||
if(WINDOWS_STORE)
|
||||
set(uwp_bindir "${CMAKE_CURRENT_BINARY_DIR}/${TARGET}.dir")
|
||||
if(NOT IS_DIRECTORY "${uwp_bindir}")
|
||||
execute_process(COMMAND "${CMAKE_COMMAND}" -E make_directory "${uwp_bindir}")
|
||||
endif()
|
||||
string(REGEX REPLACE "[_]" "" SAFE_TARGET "${TARGET}")
|
||||
file(GENERATE OUTPUT "${uwp_bindir}/${TARGET}.appxmanifest"
|
||||
INPUT "${CMAKE_CURRENT_SOURCE_DIR}/../test/uwp/Package.appxmanifest.in"
|
||||
TARGET "${TARGET}"
|
||||
)
|
||||
set_property(SOURCE "${uwp_bindir}/${TARGET}.appxmanifest" PROPERTY VS_DEPLOYMENT_CONTENT 1)
|
||||
|
||||
list(APPEND EXTRA_SOURCES "$<TARGET_OBJECTS:sdl_example_main_callbacks_uwp>")
|
||||
|
||||
list(APPEND EXTRA_SOURCES
|
||||
"${uwp_bindir}/${TARGET}.appxmanifest"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/../test/uwp/logo-50x50.png"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/../test/uwp/square-44x44.png"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/../test/uwp/square-150x150.png"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/../test/uwp/splash-620x300.png"
|
||||
)
|
||||
endif()
|
||||
if(AST_DATAFILES)
|
||||
list(APPEND EXTRA_SOURCES ${DATAFILES})
|
||||
endif()
|
||||
if(ANDROID)
|
||||
add_library(${TARGET} SHARED ${AST_SOURCES} ${EXTRA_SOURCES})
|
||||
else()
|
||||
add_executable(${TARGET} ${AST_SOURCES} ${EXTRA_SOURCES})
|
||||
endif()
|
||||
SDL_AddCommonCompilerFlags(${TARGET})
|
||||
target_include_directories(${TARGET} PRIVATE "${SDL3_SOURCE_DIR}/src/video/khronos")
|
||||
target_link_libraries(${TARGET} PRIVATE SDL3::${sdl_name_component})
|
||||
|
||||
list(APPEND SDL_EXAMPLE_EXECUTABLES ${TARGET})
|
||||
if(AST_DATAFILES)
|
||||
if(PSP OR PS2)
|
||||
add_custom_command(TARGET ${TARGET} POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} ARGS -E make_directory $<TARGET_FILE_DIR:${TARGET}>/sdl-${TARGET}
|
||||
COMMAND ${CMAKE_COMMAND} ARGS -E copy_if_different ${AST_DATAFILES} $<TARGET_FILE_DIR:${TARGET}>/sdl-${TARGET}
|
||||
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
)
|
||||
elseif(WINDOWS_STORE)
|
||||
# MSVC does build the dependent targets (or POST_BUILD commands) when building an application
|
||||
# after starting to debug. By copying the resources in a custom target, the files can be copied afterwards.
|
||||
# FIXME: find out proper way to add assets to UWP package
|
||||
cmake_minimum_required(VERSION 3.19)
|
||||
add_custom_target(zzz-resources-copy-${TARGET}
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory "$<TARGET_FILE_DIR:${TARGET}>/AppX"
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${AST_DATAFILES} "$<TARGET_FILE_DIR:${TARGET}>/AppX"
|
||||
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
)
|
||||
add_dependencies(${TARGET} zzz-resources-copy-${TARGET})
|
||||
else()
|
||||
add_dependencies(${TARGET} copy-sdl-example-resources)
|
||||
endif()
|
||||
if(APPLE)
|
||||
# Make sure resource files get installed into macOS/iOS .app bundles.
|
||||
set_target_properties(${TARGET} PROPERTIES RESOURCE "${AST_DATAFILES}")
|
||||
endif()
|
||||
if(EMSCRIPTEN)
|
||||
foreach(res IN LISTS AST_DATAFILES)
|
||||
get_filename_component(res_name "${res}" NAME)
|
||||
target_link_options(${TARGET} PRIVATE "SHELL:--embed-file ${res}@${res_name}")
|
||||
endforeach()
|
||||
endif()
|
||||
set_property(TARGET ${TARGET} APPEND PROPERTY ADDITIONAL_CLEAN_FILES "$<TARGET_FILE_DIR:${TARGET}>/$<JOIN:${AST_DATAFILES},$<SEMICOLON>$<TARGET_FILE_DIR:${TARGET}>/>")
|
||||
endif()
|
||||
|
||||
if(WINDOWS)
|
||||
# CET support was added in VS 16.7
|
||||
if(MSVC_VERSION GREATER 1926 AND CMAKE_GENERATOR_PLATFORM MATCHES "Win32|x64")
|
||||
set_property(TARGET ${TARGET} APPEND_STRING PROPERTY LINK_FLAGS " -CETCOMPAT")
|
||||
endif()
|
||||
elseif(PSP)
|
||||
target_link_libraries(${TARGET} PRIVATE GL)
|
||||
endif()
|
||||
if(WINDOWS_STORE)
|
||||
target_compile_definitions(${TARGET} PRIVATE "SDL_MAIN_NOIMPL")
|
||||
set_property(TARGET ${TARGET} PROPERTY WIN32_EXECUTABLE TRUE)
|
||||
set_property(TARGET ${TARGET} PROPERTY RUNTIME_OUTPUT_DIRECTORY "${uwp_bindir}")
|
||||
target_link_options(${TARGET} PRIVATE
|
||||
-nodefaultlib:vccorlib$<$<CONFIG:Debug>:d>
|
||||
-nodefaultlib:msvcrt$<$<CONFIG:Debug>:d>
|
||||
vccorlib$<$<CONFIG:Debug>:d>.lib
|
||||
msvcrt$<$<CONFIG:Debug>:d>.lib
|
||||
)
|
||||
endif()
|
||||
|
||||
if(EMSCRIPTEN)
|
||||
set_property(TARGET ${TARGET} PROPERTY SUFFIX ".html")
|
||||
endif()
|
||||
|
||||
if(OPENGL_FOUND)
|
||||
target_compile_definitions(${TARGET} PRIVATE HAVE_OPENGL)
|
||||
endif()
|
||||
|
||||
# FIXME: only add "${SDL3_BINARY_DIR}/include-config-$<LOWER_CASE:$<CONFIG>>" + include paths of external dependencies
|
||||
target_include_directories(${TARGET} PRIVATE "$<TARGET_PROPERTY:SDL3::${sdl_name_component},INCLUDE_DIRECTORIES>")
|
||||
endmacro()
|
||||
|
||||
add_sdl_example_executable(renderer-clear SOURCES renderer/01-clear/renderer-clear.c)
|
||||
add_sdl_example_executable(renderer-primitives SOURCES renderer/02-primitives/renderer-primitives.c)
|
||||
add_sdl_example_executable(audio-simple-playback SOURCES audio/01-simple-playback/simple-playback.c)
|
||||
add_sdl_example_executable(audio-simple-playback-callback SOURCES audio/02-simple-playback-callback/simple-playback-callback.c)
|
||||
add_sdl_example_executable(audio-load-wav SOURCES audio/03-load-wav/load-wav.c DATAFILES ${CMAKE_CURRENT_SOURCE_DIR}/../test/sample.wav)
|
||||
add_sdl_example_executable(camera-read-and-draw SOURCES camera/01-read-and-draw/read-and-draw.c)
|
||||
add_sdl_example_executable(pen-drawing-lines SOURCES pen/01-drawing-lines/drawing-lines.c)
|
||||
add_sdl_example_executable(game-snake SOURCES game/01-snake/snake.c)
|
||||
|
||||
|
||||
if(PSP)
|
||||
# Build EBOOT files if building for PSP
|
||||
foreach(APP ${SDL_EXAMPLE_EXECUTABLES})
|
||||
create_pbp_file(
|
||||
TARGET ${APP}
|
||||
TITLE SDL-${APP}
|
||||
ICON_PATH NULL
|
||||
BACKGROUND_PATH NULL
|
||||
PREVIEW_PATH NULL
|
||||
)
|
||||
add_custom_command(
|
||||
TARGET ${APP} POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory
|
||||
$<TARGET_FILE_DIR:${ARG_TARGET}>/sdl-${APP}
|
||||
)
|
||||
add_custom_command(
|
||||
TARGET ${APP} POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E rename
|
||||
$<TARGET_FILE_DIR:${ARG_TARGET}>/EBOOT.PBP
|
||||
$<TARGET_FILE_DIR:${ARG_TARGET}>/sdl-${APP}/EBOOT.PBP
|
||||
)
|
||||
if(BUILD_PRX)
|
||||
add_custom_command(
|
||||
TARGET ${APP} POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy
|
||||
$<TARGET_FILE_DIR:${ARG_TARGET}>/${APP}
|
||||
$<TARGET_FILE_DIR:${ARG_TARGET}>/sdl-${APP}/${APP}
|
||||
)
|
||||
add_custom_command(
|
||||
TARGET ${APP} POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E rename
|
||||
$<TARGET_FILE_DIR:${ARG_TARGET}>/${APP}.prx
|
||||
$<TARGET_FILE_DIR:${ARG_TARGET}>/sdl-${APP}/${APP}.prx
|
||||
)
|
||||
endif()
|
||||
add_custom_command(
|
||||
TARGET ${APP} POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E remove
|
||||
$<TARGET_FILE_DIR:${ARG_TARGET}>/PARAM.SFO
|
||||
)
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
if(N3DS)
|
||||
set(ROMFS_DIR "${CMAKE_CURRENT_BINARY_DIR}/romfs")
|
||||
file(MAKE_DIRECTORY "${ROMFS_DIR}")
|
||||
file(COPY ${RESOURCE_FILES} DESTINATION "${ROMFS_DIR}")
|
||||
|
||||
foreach(APP ${SDL_EXAMPLE_EXECUTABLES})
|
||||
get_target_property(TARGET_BINARY_DIR ${APP} BINARY_DIR)
|
||||
set(SMDH_FILE "${TARGET_BINARY_DIR}/${APP}.smdh")
|
||||
ctr_generate_smdh("${SMDH_FILE}"
|
||||
NAME "SDL-${APP}"
|
||||
DESCRIPTION "SDL3 Test suite"
|
||||
AUTHOR "SDL3 Contributors"
|
||||
ICON "${CMAKE_CURRENT_SOURCE_DIR}/../test/n3ds/logo48x48.png"
|
||||
)
|
||||
ctr_create_3dsx(
|
||||
${APP}
|
||||
ROMFS "${ROMFS_DIR}"
|
||||
SMDH "${SMDH_FILE}"
|
||||
)
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
if(RISCOS)
|
||||
set(SDL_EXAMPLE_EXECUTABLES_AIF)
|
||||
foreach(APP ${SDL_EXAMPLE_EXECUTABLES})
|
||||
set_property(TARGET ${APP} APPEND_STRING PROPERTY LINK_FLAGS " -static")
|
||||
add_custom_command(
|
||||
OUTPUT ${APP},ff8
|
||||
COMMAND elf2aif ${APP} ${APP},ff8
|
||||
DEPENDS ${APP}
|
||||
)
|
||||
add_custom_target(${APP}-aif ALL DEPENDS ${APP},ff8)
|
||||
list(APPEND SDL_EXAMPLE_EXECUTABLES_AIF ${CMAKE_CURRENT_BINARY_DIR}/${APP},ff8)
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
# Set Apple App ID / Bundle ID. This is needed to launch apps on some Apple
|
||||
# platforms (iOS, for example).
|
||||
if(APPLE)
|
||||
foreach(CURRENT_TARGET ${SDL_EXAMPLE_EXECUTABLES})
|
||||
set_target_properties("${CURRENT_TARGET}" PROPERTIES
|
||||
MACOSX_BUNDLE_GUI_IDENTIFIER "org.libsdl.${CURRENT_TARGET}"
|
||||
MACOSX_BUNDLE_BUNDLE_VERSION "${SDL3_VERSION}"
|
||||
MACOSX_BUNDLE_SHORT_VERSION_STRING "${SDL3_VERSION}"
|
||||
)
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
if(SDL_INSTALL_EXAMPLES)
|
||||
if(RISCOS)
|
||||
install(
|
||||
FILES ${SDL_EXAMPLE_EXECUTABLES_AIF}
|
||||
DESTINATION ${CMAKE_INSTALL_LIBEXECDIR}/installed-examples/SDL3
|
||||
)
|
||||
else()
|
||||
install(
|
||||
TARGETS ${SDL_EXAMPLE_EXECUTABLES}
|
||||
DESTINATION ${CMAKE_INSTALL_LIBEXECDIR}/installed-examples/SDL3
|
||||
)
|
||||
endif()
|
||||
if(MSVC)
|
||||
foreach(example IN LISTS SDL_EXAMPLE_EXECUTABLES)
|
||||
SDL_install_pdb(${example} "${CMAKE_INSTALL_LIBEXECDIR}/installed-examples/SDL3")
|
||||
endforeach()
|
||||
endif()
|
||||
install(
|
||||
FILES ${RESOURCE_FILES}
|
||||
DESTINATION ${CMAKE_INSTALL_LIBEXECDIR}/installed-examples/SDL3
|
||||
)
|
||||
endif()
|
||||
|
||||
if(ANDROID AND TARGET SDL3::Jar)
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/../cmake/android")
|
||||
find_package(SdlAndroid MODULE)
|
||||
if(SdlAndroid_FOUND)
|
||||
set(apks "")
|
||||
set(packages "")
|
||||
|
||||
include(SdlAndroidFunctions)
|
||||
sdl_create_android_debug_keystore(SDL_example-debug-keystore)
|
||||
sdl_android_compile_resources(SDL_example-resources RESFOLDER ${CMAKE_CURRENT_SOURCE_DIR}/../test/android/res)
|
||||
add_custom_target(sdl-example-apks)
|
||||
foreach(EXAMPLE ${SDL_EXAMPLE_EXECUTABLES})
|
||||
set(ANDROID_MANIFEST_APP_NAME "${EXAMPLE}")
|
||||
set(ANDROID_MANIFEST_LABEL "${EXAMPLE}")
|
||||
set(ANDROID_MANIFEST_LIB_NAME "$<TARGET_FILE_BASE_NAME:${EXAMPLE}>")
|
||||
set(ANDROID_MANIFEST_PACKAGE "org.libsdl.sdl.example.${EXAMPLE}")
|
||||
set(generated_manifest_path "${CMAKE_CURRENT_BINARY_DIR}/android/${EXAMPLE}-src/AndroidManifest.xml")
|
||||
string(REPLACE "." "/" JAVA_PACKAGE_DIR "${ANDROID_MANIFEST_PACKAGE}")
|
||||
set(GENERATED_SRC_FOLDER "${CMAKE_CURRENT_BINARY_DIR}/android/${EXAMPLE}-src")
|
||||
set(GENERATED_RES_FOLDER "${GENERATED_SRC_FOLDER}/res")
|
||||
set(JAVA_PACKAGE_DIR "${GENERATED_SRC_FOLDER}/${JAVA_PACKAGE_DIR}")
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/../test/android/cmake/SDLEntryTestActivity.java.cmake "${JAVA_PACKAGE_DIR}/SDLEntryTestActivity.java" @ONLY)
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/../test/android/cmake/SDLTestActivity.java.cmake "${JAVA_PACKAGE_DIR}/SDLTestActivity.java" @ONLY)
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/../test/android/cmake/res/values/strings.xml.cmake android/res/values/strings-${EXAMPLE}.xml @ONLY)
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/../test/android/cmake/res/xml/shortcuts.xml.cmake "${GENERATED_RES_FOLDER}/xml/shortcuts.xml" @ONLY)
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/../test/android/cmake/AndroidManifest.xml.cmake "${generated_manifest_path}" @ONLY)
|
||||
file(GENERATE
|
||||
OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/android/${EXAMPLE}-$<CONFIG>/res/values/strings.xml"
|
||||
INPUT "${CMAKE_CURRENT_BINARY_DIR}/android/res/values/strings-${EXAMPLE}.xml"
|
||||
)
|
||||
|
||||
sdl_android_compile_resources(${EXAMPLE}-resources
|
||||
RESOURCES
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/android/${EXAMPLE}-$<CONFIG>/res/values/strings.xml"
|
||||
"${GENERATED_RES_FOLDER}/xml/shortcuts.xml"
|
||||
)
|
||||
|
||||
sdl_android_link_resources(${EXAMPLE}-apk-linked
|
||||
MANIFEST "${generated_manifest_path}"
|
||||
PACKAGE ${ANDROID_MANIFEST_PACKAGE}
|
||||
RES_TARGETS SDL_example-resources ${EXAMPLE}-resources
|
||||
TARGET_SDK_VERSION 31
|
||||
)
|
||||
|
||||
set(CMAKE_JAVA_COMPILE_FLAGS "-encoding;utf-8")
|
||||
set(classes_path "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/${EXAMPLE}-java.dir/classes")
|
||||
# Some CMake versions have a slow `cmake -E make_directory` implementation
|
||||
if(NOT IS_DIRECTORY "${classes_path}")
|
||||
execute_process(COMMAND ${CMAKE_COMMAND} -E make_directory "${classes_path}")
|
||||
endif()
|
||||
set(OUT_JAR "${CMAKE_CURRENT_BINARY_DIR}/${EXAMPLE}.jar")
|
||||
add_custom_command(
|
||||
OUTPUT "${OUT_JAR}"
|
||||
COMMAND ${CMAKE_COMMAND} -E rm -rf "${classes_path}"
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory "${classes_path}"
|
||||
COMMAND ${Java_JAVAC_EXECUTABLE}
|
||||
-source 1.8 -target 1.8
|
||||
-bootclasspath "$<TARGET_PROPERTY:SDL3::Jar,JAR_FILE>"
|
||||
"${JAVA_PACKAGE_DIR}/SDLEntryTestActivity.java"
|
||||
"${JAVA_PACKAGE_DIR}/SDLTestActivity.java"
|
||||
$<TARGET_PROPERTY:${EXAMPLE}-apk-linked,JAVA_R>
|
||||
-cp "$<TARGET_PROPERTY:SDL3::Jar,JAR_FILE>:${SDL_ANDROID_PLATFORM_ANDROID_JAR}"
|
||||
-d "${classes_path}"
|
||||
COMMAND ${Java_JAR_EXECUTABLE} cf "${OUT_JAR}" -C "${classes_path}" .
|
||||
DEPENDS $<TARGET_PROPERTY:${EXAMPLE}-apk-linked,OUTPUTS> "$<TARGET_PROPERTY:SDL3::Jar,JAR_FILE>" "${JAVA_PACKAGE_DIR}/SDLTestActivity.java" "${JAVA_PACKAGE_DIR}/SDLEntryTestActivity.java"
|
||||
)
|
||||
add_custom_target(${EXAMPLE}-jar DEPENDS "${OUT_JAR}")
|
||||
set_property(TARGET ${EXAMPLE}-jar PROPERTY OUTPUT "${OUT_JAR}")
|
||||
|
||||
set(dexworkdir "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/${EXAMPLE}-dex.dir")
|
||||
# Some CMake versions have a slow `cmake -E make_directory` implementation
|
||||
if(NOT IS_DIRECTORY "${dexworkdir}")
|
||||
execute_process(COMMAND "${CMAKE_COMMAND}" -E make_directory "${dexworkdir}")
|
||||
endif()
|
||||
set(classes_dex_base_name "classes.dex")
|
||||
set(classes_dex "${dexworkdir}/${classes_dex_base_name}")
|
||||
add_custom_command(
|
||||
OUTPUT "${classes_dex}"
|
||||
COMMAND SdlAndroid::d8
|
||||
$<TARGET_PROPERTY:${EXAMPLE}-jar,OUTPUT>
|
||||
$<TARGET_PROPERTY:SDL3::Jar,JAR_FILE>
|
||||
--lib "${SDL_ANDROID_PLATFORM_ANDROID_JAR}"
|
||||
--output "${dexworkdir}"
|
||||
DEPENDS $<TARGET_PROPERTY:${EXAMPLE}-jar,OUTPUT> $<TARGET_PROPERTY:SDL3::Jar,JAR_FILE>
|
||||
)
|
||||
add_custom_target(${EXAMPLE}-dex DEPENDS "${classes_dex}")
|
||||
set_property(TARGET ${EXAMPLE}-dex PROPERTY OUTPUT "${classes_dex}")
|
||||
set_property(TARGET ${EXAMPLE}-dex PROPERTY OUTPUT_BASE_NAME "${classes_dex_base_name}")
|
||||
|
||||
sdl_add_to_apk_unaligned(${EXAMPLE}-unaligned-apk
|
||||
APK_IN ${EXAMPLE}-apk-linked
|
||||
OUTDIR "${CMAKE_CURRENT_BINARY_DIR}/intermediates"
|
||||
ASSETS ${RESOURCE_FILES}
|
||||
NATIVE_LIBS SDL3::SDL3-shared ${EXAMPLE}
|
||||
DEX ${EXAMPLE}-dex
|
||||
)
|
||||
|
||||
sdl_apk_align(${EXAMPLE}-aligned-apk ${EXAMPLE}-unaligned-apk
|
||||
OUTDIR "${CMAKE_CURRENT_BINARY_DIR}/intermediates"
|
||||
)
|
||||
sdl_apk_sign(${EXAMPLE}-apk ${EXAMPLE}-aligned-apk
|
||||
KEYSTORE SDL_example-debug-keystore
|
||||
)
|
||||
add_dependencies(sdl-example-apks ${EXAMPLE}-apk)
|
||||
|
||||
if(TARGET SdlAndroid::adb)
|
||||
add_custom_target(install-${EXAMPLE}
|
||||
COMMAND "${CMAKE_COMMAND}" -DACTION=install "-DAPKS=$<TARGET_PROPERTY:${EXAMPLE}-apk,OUTPUT>" -P "${SDL3_SOURCE_DIR}/cmake/android/SdlAndroidScript.cmake"
|
||||
DEPENDS "${EXAMPLE}-apk"
|
||||
)
|
||||
add_custom_target(start-${EXAMPLE}
|
||||
COMMAND "${ADB_BIN}" shell am start-activity -S "${ANDROID_MANIFEST_PACKAGE}/.SDLTestActivity"
|
||||
)
|
||||
add_custom_target(build-install-start-${EXAMPLE}
|
||||
COMMAND "${CMAKE_COMMAND}" -DACTION=build-install-run "-DEXECUTABLES=${EXAMPLE}" "-DBUILD_FOLDER=${CMAKE_BINARY_DIR}" -P "${SDL3_SOURCE_DIR}/cmake/android/SdlAndroidScript.cmake"
|
||||
)
|
||||
endif()
|
||||
|
||||
list(APPEND packages "${ANDROID_MANIFEST_PACKAGE}")
|
||||
list(APPEND install_targets install-${EXAMPLE})
|
||||
endforeach()
|
||||
|
||||
if(TARGET SdlAndroid::adb)
|
||||
add_custom_target(install-sdl-example-apks
|
||||
DEPENDS ${install_targets}
|
||||
VERBATIM
|
||||
)
|
||||
add_custom_target(uninstall-sdl-example-apks
|
||||
COMMAND "${CMAKE_COMMAND}" "-DADB=$<TARGET_FILE:SdlAndroid::adb>" -DACTION=uninstall "-DPACKAGES=${packages}" -P "${SDL3_SOURCE_DIR}/cmake/android/SdlAndroidScript.cmake"
|
||||
VERBATIM
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
67
examples/README.md
Normal file
67
examples/README.md
Normal file
@@ -0,0 +1,67 @@
|
||||
# Examples
|
||||
|
||||
## What is this?
|
||||
|
||||
In here are a collection of standalone SDL application examples. Unless
|
||||
otherwise stated, they should work on all supported platforms out of the box.
|
||||
If they don't [please file a bug to let us know](https://github.com/libsdl-org/SDL/issues/new).
|
||||
|
||||
|
||||
## What is this SDL_AppIterate thing?
|
||||
|
||||
SDL can optionally build apps as a collection of callbacks instead of the
|
||||
usual program structure that starts and ends in a function called `main`.
|
||||
The examples use this format for two reasons.
|
||||
|
||||
First, it allows the examples to work when built as web applications without
|
||||
a pile of ugly `#ifdef`s, and all of these examples are published on the web
|
||||
at [examples.libsdl.org](https://examples.libsdl.org/), so you can easily see
|
||||
them in action.
|
||||
|
||||
Second, it's example code! The callbacks let us cleanly break the program up
|
||||
into the four logical pieces most apps care about:
|
||||
|
||||
- Program startup
|
||||
- Event handling
|
||||
- What the program actually does in a single frame
|
||||
- Program shutdown
|
||||
|
||||
A detailed technical explanation of these callbacks is in
|
||||
docs/README-main-functions.md (or view that page on the web on
|
||||
[the wiki](https://wiki.libsdl.org/SDL3/README/main-functions#main-callbacks-in-sdl3)).
|
||||
|
||||
|
||||
## I would like to build and run these examples myself.
|
||||
|
||||
When you build SDL with CMake, you can add `-DSDL_EXAMPLES=On` to the
|
||||
CMake command line. When you build SDL, these examples will be built with it.
|
||||
|
||||
But most of these can just be built as a single .c file, as long as you point
|
||||
your compiler at SDL3's headers and link against SDL.
|
||||
|
||||
|
||||
## What is the license on the example code? Can I paste this into my project?
|
||||
|
||||
All code in the examples directory is considered public domain! You can do
|
||||
anything you like with it, including copy/paste it into your closed-source
|
||||
project, sell it, and pretend you wrote it yourself. We do not require you to
|
||||
give us credit for this code (but we always appreciate if you do!).
|
||||
|
||||
This is only true for the examples directory. The rest of SDL falls under the
|
||||
[zlib license](https://github.com/libsdl-org/SDL/blob/main/LICENSE.txt).
|
||||
|
||||
|
||||
## What is template.html and highlight-plugin.lua in this directory?
|
||||
|
||||
This is what [examples.libsdl.org](https://examples.libsdl.org/) uses when
|
||||
generating the web versions of these example programs. You can ignore this,
|
||||
unless you are improving it, in which case we definitely would love to hear
|
||||
from you!
|
||||
|
||||
|
||||
## What is template.c in this directory?
|
||||
|
||||
If writing new examples, this is the skeleton code we start from, to keep
|
||||
everything consistent. You can ignore it.
|
||||
|
||||
|
||||
5
examples/audio/01-simple-playback/README.txt
Normal file
5
examples/audio/01-simple-playback/README.txt
Normal file
@@ -0,0 +1,5 @@
|
||||
If you're running this in a web browser, you need to click the window before you'll hear anything!
|
||||
|
||||
This example code creates an simple audio stream for playing sound, and
|
||||
generates a sine wave sound effect for it to play as time goes on. This is the
|
||||
simplest way to get up and running with procedural sound.
|
||||
99
examples/audio/01-simple-playback/simple-playback.c
Normal file
99
examples/audio/01-simple-playback/simple-playback.c
Normal file
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* This example code creates an simple audio stream for playing sound, and
|
||||
* generates a sine wave sound effect for it to play as time goes on. This
|
||||
* is the simplest way to get up and running with procedural sound.
|
||||
*
|
||||
* This code is public domain. Feel free to use it for any purpose!
|
||||
*/
|
||||
|
||||
#define SDL_MAIN_USE_CALLBACKS 1 /* use the callbacks instead of main() */
|
||||
#include <SDL3/SDL.h>
|
||||
#include <SDL3/SDL_main.h>
|
||||
|
||||
/* We will use this renderer to draw into this window every frame. */
|
||||
static SDL_Window *window = NULL;
|
||||
static SDL_Renderer *renderer = NULL;
|
||||
static SDL_AudioStream *stream = NULL;
|
||||
static int total_samples_generated = 0;
|
||||
|
||||
/* This function runs once at startup. */
|
||||
SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[])
|
||||
{
|
||||
SDL_AudioSpec spec;
|
||||
|
||||
if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO)) {
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Couldn't initialize SDL!", SDL_GetError(), NULL);
|
||||
return SDL_APP_FAILURE;
|
||||
}
|
||||
|
||||
/* we don't _need_ a window for audio-only things but it's good policy to have one. */
|
||||
if (!SDL_CreateWindowAndRenderer("examples/audio/simple-playback", 640, 480, 0, &window, &renderer)) {
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Couldn't create window/renderer!", SDL_GetError(), NULL);
|
||||
return SDL_APP_FAILURE;
|
||||
}
|
||||
|
||||
/* We're just playing a single thing here, so we'll use the simplified option.
|
||||
We are always going to feed audio in as mono, float32 data at 8000Hz.
|
||||
The stream will convert it to whatever the hardware wants on the other side. */
|
||||
spec.channels = 1;
|
||||
spec.format = SDL_AUDIO_F32;
|
||||
spec.freq = 8000;
|
||||
stream = SDL_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &spec, NULL, NULL);
|
||||
if (!stream) {
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Couldn't create audio stream!", SDL_GetError(), window);
|
||||
return SDL_APP_FAILURE;
|
||||
}
|
||||
|
||||
/* SDL_OpenAudioDeviceStream starts the device paused. You have to tell it to start! */
|
||||
SDL_ResumeAudioStreamDevice(stream);
|
||||
|
||||
return SDL_APP_CONTINUE; /* carry on with the program! */
|
||||
}
|
||||
|
||||
/* This function runs when a new event (mouse input, keypresses, etc) occurs. */
|
||||
SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event)
|
||||
{
|
||||
if (event->type == SDL_EVENT_QUIT) {
|
||||
return SDL_APP_SUCCESS; /* end the program, reporting success to the OS. */
|
||||
}
|
||||
return SDL_APP_CONTINUE; /* carry on with the program! */
|
||||
}
|
||||
|
||||
/* This function runs once per frame, and is the heart of the program. */
|
||||
SDL_AppResult SDL_AppIterate(void *appstate)
|
||||
{
|
||||
/* see if we need to feed the audio stream more data yet.
|
||||
We're being lazy here, but if there's less than half a second queued, generate more.
|
||||
A sine wave is unchanging audio--easy to stream--but for video games, you'll want
|
||||
to generate significantly _less_ audio ahead of time! */
|
||||
const int minimum_audio = (8000 * sizeof (float)) / 2; /* 8000 float samples per second. Half of that. */
|
||||
if (SDL_GetAudioStreamAvailable(stream) < minimum_audio) {
|
||||
static float samples[512]; /* this will feed 512 samples each frame until we get to our maximum. */
|
||||
int i;
|
||||
|
||||
for (i = 0; i < SDL_arraysize(samples); i++) {
|
||||
/* You don't have to care about this math; we're just generating a simple sine wave as we go.
|
||||
https://en.wikipedia.org/wiki/Sine_wave */
|
||||
const float time = total_samples_generated / 8000.0f;
|
||||
const int sine_freq = 500; /* run the wave at 500Hz */
|
||||
samples[i] = SDL_sinf(6.283185f * sine_freq * time);
|
||||
total_samples_generated++;
|
||||
}
|
||||
|
||||
/* feed the new data to the stream. It will queue at the end, and trickle out as the hardware needs more data. */
|
||||
SDL_PutAudioStreamData(stream, samples, sizeof (samples));
|
||||
}
|
||||
|
||||
/* we're not doing anything with the renderer, so just blank it out. */
|
||||
SDL_RenderClear(renderer);
|
||||
SDL_RenderPresent(renderer);
|
||||
|
||||
return SDL_APP_CONTINUE; /* carry on with the program! */
|
||||
}
|
||||
|
||||
/* This function runs once at shutdown. */
|
||||
void SDL_AppQuit(void *appstate)
|
||||
{
|
||||
/* SDL will clean up the window/renderer for us. */
|
||||
}
|
||||
|
||||
5
examples/audio/02-simple-playback-callback/README.txt
Normal file
5
examples/audio/02-simple-playback-callback/README.txt
Normal file
@@ -0,0 +1,5 @@
|
||||
If you're running this in a web browser, you need to click the window before you'll hear anything!
|
||||
|
||||
This example code creates an simple audio stream for playing sound, and
|
||||
generates a sine wave sound effect for it to play as time goes on. Unlike
|
||||
the previous example, this uses a callback to generate sound.
|
||||
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* This example code creates an simple audio stream for playing sound, and
|
||||
* generates a sine wave sound effect for it to play as time goes on. Unlike
|
||||
* the previous example, this uses a callback to generate sound.
|
||||
*
|
||||
* This might be the path of least resistance if you're moving an SDL2
|
||||
* program's audio code to SDL3.
|
||||
*
|
||||
* This code is public domain. Feel free to use it for any purpose!
|
||||
*/
|
||||
|
||||
#define SDL_MAIN_USE_CALLBACKS 1 /* use the callbacks instead of main() */
|
||||
#include <SDL3/SDL.h>
|
||||
#include <SDL3/SDL_main.h>
|
||||
|
||||
/* We will use this renderer to draw into this window every frame. */
|
||||
static SDL_Window *window = NULL;
|
||||
static SDL_Renderer *renderer = NULL;
|
||||
static SDL_AudioStream *stream = NULL;
|
||||
static int total_samples_generated = 0;
|
||||
|
||||
/* this function will be called (usually in a background thread) when the audio stream is consuming data. */
|
||||
static void SDLCALL FeedTheAudioStreamMore(void *userdata, SDL_AudioStream *astream, int additional_amount, int total_amount)
|
||||
{
|
||||
/* total_amount is how much data the audio stream is eating right now, additional_amount is how much more it needs
|
||||
than what it currently has queued (which might be zero!). You can supply any amount of data here; it will take what
|
||||
it needs and use the extra later. If you don't give it enough, it will take everything and then feed silence to the
|
||||
hardware for the rest. Ideally, though, we always give it what it needs and no extra, so we aren't buffering more
|
||||
than necessary. */
|
||||
additional_amount /= sizeof (float); /* convert from bytes to samples */
|
||||
while (additional_amount > 0) {
|
||||
float samples[128]; /* this will feed 128 samples each iteration until we have enough. */
|
||||
const int total = SDL_min(additional_amount, SDL_arraysize(samples));
|
||||
int i;
|
||||
|
||||
for (i = 0; i < total; i++) {
|
||||
/* You don't have to care about this math; we're just generating a simple sine wave as we go.
|
||||
https://en.wikipedia.org/wiki/Sine_wave */
|
||||
const float time = total_samples_generated / 8000.0f;
|
||||
const int sine_freq = 500; /* run the wave at 500Hz */
|
||||
samples[i] = SDL_sinf(6.283185f * sine_freq * time);
|
||||
total_samples_generated++;
|
||||
}
|
||||
|
||||
/* feed the new data to the stream. It will queue at the end, and trickle out as the hardware needs more data. */
|
||||
SDL_PutAudioStreamData(astream, samples, total * sizeof (float));
|
||||
additional_amount -= total; /* subtract what we've just fed the stream. */
|
||||
}
|
||||
}
|
||||
|
||||
/* This function runs once at startup. */
|
||||
SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[])
|
||||
{
|
||||
SDL_AudioSpec spec;
|
||||
|
||||
if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO)) {
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Couldn't initialize SDL!", SDL_GetError(), NULL);
|
||||
return SDL_APP_FAILURE;
|
||||
}
|
||||
|
||||
/* we don't _need_ a window for audio-only things but it's good policy to have one. */
|
||||
if (!SDL_CreateWindowAndRenderer("examples/audio/simple-playback-callback", 640, 480, 0, &window, &renderer)) {
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Couldn't create window/renderer!", SDL_GetError(), NULL);
|
||||
return SDL_APP_FAILURE;
|
||||
}
|
||||
|
||||
/* We're just playing a single thing here, so we'll use the simplified option.
|
||||
We are always going to feed audio in as mono, float32 data at 8000Hz.
|
||||
The stream will convert it to whatever the hardware wants on the other side. */
|
||||
spec.channels = 1;
|
||||
spec.format = SDL_AUDIO_F32;
|
||||
spec.freq = 8000;
|
||||
stream = SDL_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &spec, FeedTheAudioStreamMore, NULL);
|
||||
if (!stream) {
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Couldn't create audio stream!", SDL_GetError(), window);
|
||||
return SDL_APP_FAILURE;
|
||||
}
|
||||
|
||||
/* SDL_OpenAudioDeviceStream starts the device paused. You have to tell it to start! */
|
||||
SDL_ResumeAudioStreamDevice(stream);
|
||||
|
||||
return SDL_APP_CONTINUE; /* carry on with the program! */
|
||||
}
|
||||
|
||||
/* This function runs when a new event (mouse input, keypresses, etc) occurs. */
|
||||
SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event)
|
||||
{
|
||||
if (event->type == SDL_EVENT_QUIT) {
|
||||
return SDL_APP_SUCCESS; /* end the program, reporting success to the OS. */
|
||||
}
|
||||
return SDL_APP_CONTINUE; /* carry on with the program! */
|
||||
}
|
||||
|
||||
/* This function runs once per frame, and is the heart of the program. */
|
||||
SDL_AppResult SDL_AppIterate(void *appstate)
|
||||
{
|
||||
/* we're not doing anything with the renderer, so just blank it out. */
|
||||
SDL_RenderClear(renderer);
|
||||
SDL_RenderPresent(renderer);
|
||||
|
||||
/* all the work of feeding the audio stream is happening in a callback in a background thread. */
|
||||
|
||||
return SDL_APP_CONTINUE; /* carry on with the program! */
|
||||
}
|
||||
|
||||
/* This function runs once at shutdown. */
|
||||
void SDL_AppQuit(void *appstate)
|
||||
{
|
||||
/* SDL will clean up the window/renderer for us. */
|
||||
}
|
||||
|
||||
5
examples/audio/03-load-wav/README.txt
Normal file
5
examples/audio/03-load-wav/README.txt
Normal file
@@ -0,0 +1,5 @@
|
||||
If you're running this in a web browser, you need to click the window before you'll hear anything!
|
||||
|
||||
This example code creates an simple audio stream for playing sound, and
|
||||
loads a .wav file that is pushed through the stream in a loop.
|
||||
|
||||
101
examples/audio/03-load-wav/load-wav.c
Normal file
101
examples/audio/03-load-wav/load-wav.c
Normal file
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* This example code creates an simple audio stream for playing sound, and
|
||||
* loads a .wav file that is pushed through the stream in a loop.
|
||||
*
|
||||
* This code is public domain. Feel free to use it for any purpose!
|
||||
*
|
||||
* The .wav file is a sample from Will Provost's song, The Living Proof,
|
||||
* used with permission.
|
||||
*
|
||||
* From the album The Living Proof
|
||||
* Publisher: 5 Guys Named Will
|
||||
* Copyright 1996 Will Provost
|
||||
* https://itunes.apple.com/us/album/the-living-proof/id4153978
|
||||
* http://www.amazon.com/The-Living-Proof-Will-Provost/dp/B00004R8RH
|
||||
*/
|
||||
|
||||
#define SDL_MAIN_USE_CALLBACKS 1 /* use the callbacks instead of main() */
|
||||
#include <SDL3/SDL.h>
|
||||
#include <SDL3/SDL_main.h>
|
||||
|
||||
/* We will use this renderer to draw into this window every frame. */
|
||||
static SDL_Window *window = NULL;
|
||||
static SDL_Renderer *renderer = NULL;
|
||||
static SDL_AudioStream *stream = NULL;
|
||||
static Uint8 *wav_data = NULL;
|
||||
static Uint32 wav_data_len = 0;
|
||||
|
||||
/* This function runs once at startup. */
|
||||
SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[])
|
||||
{
|
||||
SDL_AudioSpec spec;
|
||||
char *wav_path = NULL;
|
||||
|
||||
if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO)) {
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Couldn't initialize SDL!", SDL_GetError(), NULL);
|
||||
return SDL_APP_FAILURE;
|
||||
}
|
||||
|
||||
/* we don't _need_ a window for audio-only things but it's good policy to have one. */
|
||||
if (!SDL_CreateWindowAndRenderer("examples/audio/load-wav", 640, 480, 0, &window, &renderer)) {
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Couldn't create window/renderer!", SDL_GetError(), NULL);
|
||||
return SDL_APP_FAILURE;
|
||||
}
|
||||
|
||||
/* Load the .wav file from wherever the app is being run from. */
|
||||
SDL_asprintf(&wav_path, "%ssample.wav", SDL_GetBasePath()); /* allocate a string of the full file path */
|
||||
if (!SDL_LoadWAV(wav_path, &spec, &wav_data, &wav_data_len)) {
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Couldn't load .wav file!", SDL_GetError(), NULL);
|
||||
return SDL_APP_FAILURE;
|
||||
}
|
||||
|
||||
SDL_free(wav_path); /* done with this string. */
|
||||
|
||||
/* Create our audio stream in the same format as the .wav file. It'll convert to what the audio hardware wants. */
|
||||
stream = SDL_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &spec, NULL, NULL);
|
||||
if (!stream) {
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Couldn't create audio stream!", SDL_GetError(), window);
|
||||
return SDL_APP_FAILURE;
|
||||
}
|
||||
|
||||
/* SDL_OpenAudioDeviceStream starts the device paused. You have to tell it to start! */
|
||||
SDL_ResumeAudioStreamDevice(stream);
|
||||
|
||||
return SDL_APP_CONTINUE; /* carry on with the program! */
|
||||
}
|
||||
|
||||
/* This function runs when a new event (mouse input, keypresses, etc) occurs. */
|
||||
SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event)
|
||||
{
|
||||
if (event->type == SDL_EVENT_QUIT) {
|
||||
return SDL_APP_SUCCESS; /* end the program, reporting success to the OS. */
|
||||
}
|
||||
return SDL_APP_CONTINUE; /* carry on with the program! */
|
||||
}
|
||||
|
||||
/* This function runs once per frame, and is the heart of the program. */
|
||||
SDL_AppResult SDL_AppIterate(void *appstate)
|
||||
{
|
||||
/* see if we need to feed the audio stream more data yet.
|
||||
We're being lazy here, but if there's less than the entire wav file left to play,
|
||||
just shove a whole copy of it into the queue, so we always have _tons_ of
|
||||
data queued for playback. */
|
||||
if (SDL_GetAudioStreamAvailable(stream) < (int)wav_data_len) {
|
||||
/* feed more data to the stream. It will queue at the end, and trickle out as the hardware needs more data. */
|
||||
SDL_PutAudioStreamData(stream, wav_data, wav_data_len);
|
||||
}
|
||||
|
||||
/* we're not doing anything with the renderer, so just blank it out. */
|
||||
SDL_RenderClear(renderer);
|
||||
SDL_RenderPresent(renderer);
|
||||
|
||||
return SDL_APP_CONTINUE; /* carry on with the program! */
|
||||
}
|
||||
|
||||
/* This function runs once at shutdown. */
|
||||
void SDL_AppQuit(void *appstate)
|
||||
{
|
||||
SDL_free(wav_data); /* strictly speaking, this isn't necessary because the process is ending, but it's good policy. */
|
||||
/* SDL will clean up the window/renderer for us. */
|
||||
}
|
||||
|
||||
1
examples/camera/01-read-and-draw/README.txt
Normal file
1
examples/camera/01-read-and-draw/README.txt
Normal file
@@ -0,0 +1 @@
|
||||
This reads from a webcam and draws frames of video to the screen.
|
||||
112
examples/camera/01-read-and-draw/read-and-draw.c
Normal file
112
examples/camera/01-read-and-draw/read-and-draw.c
Normal file
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* This example code reads frames from a camera and draws it to the screen.
|
||||
*
|
||||
* This is a very simple approach that is often Good Enough. You can get
|
||||
* fancier with this: multiple cameras, front/back facing cameras on phones,
|
||||
* color spaces, choosing formats and framerates...this just requests
|
||||
* _anything_ and goes with what it is handed.
|
||||
*
|
||||
* This code is public domain. Feel free to use it for any purpose!
|
||||
*/
|
||||
|
||||
#define SDL_MAIN_USE_CALLBACKS 1 /* use the callbacks instead of main() */
|
||||
#include <SDL3/SDL.h>
|
||||
#include <SDL3/SDL_main.h>
|
||||
|
||||
/* We will use this renderer to draw into this window every frame. */
|
||||
static SDL_Window *window = NULL;
|
||||
static SDL_Renderer *renderer = NULL;
|
||||
static SDL_Camera *camera = NULL;
|
||||
static SDL_Texture *texture = NULL;
|
||||
|
||||
|
||||
/* This function runs once at startup. */
|
||||
SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[])
|
||||
{
|
||||
SDL_CameraID *devices = NULL;
|
||||
int devcount = 0;
|
||||
|
||||
if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_CAMERA)) {
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Couldn't initialize SDL!", SDL_GetError(), NULL);
|
||||
return SDL_APP_FAILURE;
|
||||
}
|
||||
|
||||
if (!SDL_CreateWindowAndRenderer("examples/camera/read-and-draw", 640, 480, 0, &window, &renderer)) {
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Couldn't create window/renderer!", SDL_GetError(), NULL);
|
||||
return SDL_APP_FAILURE;
|
||||
}
|
||||
|
||||
devices = SDL_GetCameras(&devcount);
|
||||
if (devices == NULL) {
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Couldn't enumerate camera devices!", SDL_GetError(), window);
|
||||
return SDL_APP_FAILURE;
|
||||
} else if (devcount == 0) {
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Couldn't find any camera devices!", "Please connect a camera and try again.", window);
|
||||
return SDL_APP_FAILURE;
|
||||
}
|
||||
|
||||
camera = SDL_OpenCamera(devices[0], NULL); // just take the first thing we see in any format it wants.
|
||||
SDL_free(devices);
|
||||
if (camera == NULL) {
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Couldn't open camera!", SDL_GetError(), window);
|
||||
return SDL_APP_FAILURE;
|
||||
}
|
||||
|
||||
return SDL_APP_CONTINUE; /* carry on with the program! */
|
||||
}
|
||||
|
||||
/* This function runs when a new event (mouse input, keypresses, etc) occurs. */
|
||||
SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event)
|
||||
{
|
||||
if (event->type == SDL_EVENT_QUIT) {
|
||||
return SDL_APP_SUCCESS; /* end the program, reporting success to the OS. */
|
||||
} else if (event->type == SDL_EVENT_CAMERA_DEVICE_APPROVED) {
|
||||
SDL_Log("Camera use approved by user!");
|
||||
} else if (event->type == SDL_EVENT_CAMERA_DEVICE_DENIED) {
|
||||
SDL_Log("Camera use denied by user!");
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Camera permission denied!", "User denied access to the camera!", window);
|
||||
return SDL_APP_FAILURE;
|
||||
}
|
||||
return SDL_APP_CONTINUE; /* carry on with the program! */
|
||||
}
|
||||
|
||||
/* This function runs once per frame, and is the heart of the program. */
|
||||
SDL_AppResult SDL_AppIterate(void *appstate)
|
||||
{
|
||||
Uint64 timestampNS = 0;
|
||||
SDL_Surface *frame = SDL_AcquireCameraFrame(camera, ×tampNS);
|
||||
|
||||
if (frame != NULL) {
|
||||
/* Some platforms (like Emscripten) don't know _what_ the camera offers
|
||||
until the user gives permission, so we build the texture and resize
|
||||
the window when we get a first frame from the camera. */
|
||||
if (!texture) {
|
||||
SDL_SetWindowSize(window, frame->w, frame->h); /* Resize the window to match */
|
||||
texture = SDL_CreateTexture(renderer, frame->format, SDL_TEXTUREACCESS_STREAMING, frame->w, frame->h);
|
||||
}
|
||||
|
||||
if (texture) {
|
||||
SDL_UpdateTexture(texture, NULL, frame->pixels, frame->pitch);
|
||||
}
|
||||
|
||||
SDL_ReleaseCameraFrame(camera, frame);
|
||||
}
|
||||
|
||||
SDL_SetRenderDrawColor(renderer, 0x99, 0x99, 0x99, 255);
|
||||
SDL_RenderClear(renderer);
|
||||
if (texture) { /* draw the latest camera frame, if available. */
|
||||
SDL_RenderTexture(renderer, texture, NULL, NULL);
|
||||
}
|
||||
SDL_RenderPresent(renderer);
|
||||
|
||||
return SDL_APP_CONTINUE; /* carry on with the program! */
|
||||
}
|
||||
|
||||
/* This function runs once at shutdown. */
|
||||
void SDL_AppQuit(void *appstate)
|
||||
{
|
||||
SDL_CloseCamera(camera);
|
||||
SDL_DestroyTexture(texture);
|
||||
/* SDL will clean up the window/renderer for us. */
|
||||
}
|
||||
|
||||
1
examples/game/01-snake/README.txt
Normal file
1
examples/game/01-snake/README.txt
Normal file
@@ -0,0 +1 @@
|
||||
A complete game of Snake, written in SDL.
|
||||
332
examples/game/01-snake/snake.c
Normal file
332
examples/game/01-snake/snake.c
Normal file
@@ -0,0 +1,332 @@
|
||||
/*
|
||||
* Logic implementation of the Snake game. It is designed to efficiently
|
||||
* represent in memory the state of the game.
|
||||
*
|
||||
* This code is public domain. Feel free to use it for any purpose!
|
||||
*/
|
||||
|
||||
#define SDL_MAIN_USE_CALLBACKS 1 /* use the callbacks instead of main() */
|
||||
#include <SDL3/SDL.h>
|
||||
#include <SDL3/SDL_main.h>
|
||||
|
||||
#define STEP_RATE_IN_MILLISECONDS 125
|
||||
#define SNAKE_BLOCK_SIZE_IN_PIXELS 24
|
||||
#define SDL_WINDOW_WIDTH (SNAKE_BLOCK_SIZE_IN_PIXELS * SNAKE_GAME_WIDTH)
|
||||
#define SDL_WINDOW_HEIGHT (SNAKE_BLOCK_SIZE_IN_PIXELS * SNAKE_GAME_HEIGHT)
|
||||
|
||||
#define SNAKE_GAME_WIDTH 24U
|
||||
#define SNAKE_GAME_HEIGHT 18U
|
||||
#define SNAKE_MATRIX_SIZE (SNAKE_GAME_WIDTH * SNAKE_GAME_HEIGHT)
|
||||
|
||||
#define THREE_BITS 0x7U /* ~CHAR_MAX >> (CHAR_BIT - SNAKE_CELL_MAX_BITS) */
|
||||
#define SHIFT(x, y) (((x) + ((y) * SNAKE_GAME_WIDTH)) * SNAKE_CELL_MAX_BITS)
|
||||
|
||||
typedef enum
|
||||
{
|
||||
SNAKE_CELL_NOTHING = 0U,
|
||||
SNAKE_CELL_SRIGHT = 1U,
|
||||
SNAKE_CELL_SUP = 2U,
|
||||
SNAKE_CELL_SLEFT = 3U,
|
||||
SNAKE_CELL_SDOWN = 4U,
|
||||
SNAKE_CELL_FOOD = 5U
|
||||
} SnakeCell;
|
||||
|
||||
#define SNAKE_CELL_MAX_BITS 3U /* floor(log2(SNAKE_CELL_FOOD)) + 1 */
|
||||
|
||||
typedef enum
|
||||
{
|
||||
SNAKE_DIR_RIGHT,
|
||||
SNAKE_DIR_UP,
|
||||
SNAKE_DIR_LEFT,
|
||||
SNAKE_DIR_DOWN
|
||||
} SnakeDirection;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
unsigned char cells[(SNAKE_MATRIX_SIZE * SNAKE_CELL_MAX_BITS) / 8U];
|
||||
char head_xpos;
|
||||
char head_ypos;
|
||||
char tail_xpos;
|
||||
char tail_ypos;
|
||||
char next_dir;
|
||||
char inhibit_tail_step;
|
||||
unsigned occupied_cells;
|
||||
} SnakeContext;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
SDL_Window *window;
|
||||
SDL_Renderer *renderer;
|
||||
SDL_TimerID step_timer;
|
||||
SnakeContext snake_ctx;
|
||||
} AppState;
|
||||
|
||||
SnakeCell snake_cell_at(const SnakeContext *ctx, char x, char y)
|
||||
{
|
||||
const int shift = SHIFT(x, y);
|
||||
unsigned short range;
|
||||
SDL_memcpy(&range, ctx->cells + (shift / 8), sizeof(range));
|
||||
return (SnakeCell)((range >> (shift % 8)) & THREE_BITS);
|
||||
}
|
||||
|
||||
static void set_rect_xy_(SDL_FRect *r, short x, short y)
|
||||
{
|
||||
r->x = (float)(x * SNAKE_BLOCK_SIZE_IN_PIXELS);
|
||||
r->y = (float)(y * SNAKE_BLOCK_SIZE_IN_PIXELS);
|
||||
}
|
||||
|
||||
static void put_cell_at_(SnakeContext *ctx, char x, char y, SnakeCell ct)
|
||||
{
|
||||
const int shift = SHIFT(x, y);
|
||||
const int adjust = shift % 8;
|
||||
unsigned char *const pos = ctx->cells + (shift / 8);
|
||||
unsigned short range;
|
||||
SDL_memcpy(&range, pos, sizeof(range));
|
||||
range &= ~(THREE_BITS << adjust); /* clear bits */
|
||||
range |= (ct & THREE_BITS) << adjust;
|
||||
SDL_memcpy(pos, &range, sizeof(range));
|
||||
}
|
||||
|
||||
static int are_cells_full_(SnakeContext *ctx)
|
||||
{
|
||||
return ctx->occupied_cells == SNAKE_GAME_WIDTH * SNAKE_GAME_HEIGHT;
|
||||
}
|
||||
|
||||
static void new_food_pos_(SnakeContext *ctx)
|
||||
{
|
||||
while (SDL_TRUE) {
|
||||
const char x = (char) SDL_rand(SNAKE_GAME_WIDTH);
|
||||
const char y = (char) SDL_rand(SNAKE_GAME_HEIGHT);
|
||||
if (snake_cell_at(ctx, x, y) == SNAKE_CELL_NOTHING) {
|
||||
put_cell_at_(ctx, x, y, SNAKE_CELL_FOOD);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void snake_initialize(SnakeContext *ctx)
|
||||
{
|
||||
int i;
|
||||
SDL_zeroa(ctx->cells);
|
||||
ctx->head_xpos = ctx->tail_xpos = SNAKE_GAME_WIDTH / 2;
|
||||
ctx->head_ypos = ctx->tail_ypos = SNAKE_GAME_HEIGHT / 2;
|
||||
ctx->next_dir = SNAKE_DIR_RIGHT;
|
||||
ctx->inhibit_tail_step = ctx->occupied_cells = 4;
|
||||
--ctx->occupied_cells;
|
||||
put_cell_at_(ctx, ctx->tail_xpos, ctx->tail_ypos, SNAKE_CELL_SRIGHT);
|
||||
for (i = 0; i < 4; i++) {
|
||||
new_food_pos_(ctx);
|
||||
++ctx->occupied_cells;
|
||||
}
|
||||
}
|
||||
|
||||
void snake_redir(SnakeContext *ctx, SnakeDirection dir)
|
||||
{
|
||||
SnakeCell ct = snake_cell_at(ctx, ctx->head_xpos, ctx->head_ypos);
|
||||
if ((dir == SNAKE_DIR_RIGHT && ct != SNAKE_CELL_SLEFT) ||
|
||||
(dir == SNAKE_DIR_UP && ct != SNAKE_CELL_SDOWN) ||
|
||||
(dir == SNAKE_DIR_LEFT && ct != SNAKE_CELL_SRIGHT) ||
|
||||
(dir == SNAKE_DIR_DOWN && ct != SNAKE_CELL_SUP)) {
|
||||
ctx->next_dir = dir;
|
||||
}
|
||||
}
|
||||
|
||||
static void wrap_around_(char *val, char max)
|
||||
{
|
||||
if (*val < 0) {
|
||||
*val = max - 1;
|
||||
} else if (*val > max - 1) {
|
||||
*val = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void snake_step(SnakeContext *ctx)
|
||||
{
|
||||
const SnakeCell dir_as_cell = (SnakeCell)(ctx->next_dir + 1);
|
||||
SnakeCell ct;
|
||||
char prev_xpos;
|
||||
char prev_ypos;
|
||||
/* Move tail forward */
|
||||
if (--ctx->inhibit_tail_step == 0) {
|
||||
++ctx->inhibit_tail_step;
|
||||
ct = snake_cell_at(ctx, ctx->tail_xpos, ctx->tail_ypos);
|
||||
put_cell_at_(ctx, ctx->tail_xpos, ctx->tail_ypos, SNAKE_CELL_NOTHING);
|
||||
switch (ct) {
|
||||
case SNAKE_CELL_SRIGHT:
|
||||
ctx->tail_xpos++;
|
||||
break;
|
||||
case SNAKE_CELL_SUP:
|
||||
ctx->tail_ypos--;
|
||||
break;
|
||||
case SNAKE_CELL_SLEFT:
|
||||
ctx->tail_xpos--;
|
||||
break;
|
||||
case SNAKE_CELL_SDOWN:
|
||||
ctx->tail_ypos++;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
wrap_around_(&ctx->tail_xpos, SNAKE_GAME_WIDTH);
|
||||
wrap_around_(&ctx->tail_ypos, SNAKE_GAME_HEIGHT);
|
||||
}
|
||||
/* Move head forward */
|
||||
prev_xpos = ctx->head_xpos;
|
||||
prev_ypos = ctx->head_ypos;
|
||||
switch (ctx->next_dir) {
|
||||
case SNAKE_DIR_RIGHT:
|
||||
++ctx->head_xpos;
|
||||
break;
|
||||
case SNAKE_DIR_UP:
|
||||
--ctx->head_ypos;
|
||||
break;
|
||||
case SNAKE_DIR_LEFT:
|
||||
--ctx->head_xpos;
|
||||
break;
|
||||
case SNAKE_DIR_DOWN:
|
||||
++ctx->head_ypos;
|
||||
break;
|
||||
}
|
||||
wrap_around_(&ctx->head_xpos, SNAKE_GAME_WIDTH);
|
||||
wrap_around_(&ctx->head_ypos, SNAKE_GAME_HEIGHT);
|
||||
/* Collisions */
|
||||
ct = snake_cell_at(ctx, ctx->head_xpos, ctx->head_ypos);
|
||||
if (ct != SNAKE_CELL_NOTHING && ct != SNAKE_CELL_FOOD) {
|
||||
snake_initialize(ctx);
|
||||
return;
|
||||
}
|
||||
put_cell_at_(ctx, prev_xpos, prev_ypos, dir_as_cell);
|
||||
put_cell_at_(ctx, ctx->head_xpos, ctx->head_ypos, dir_as_cell);
|
||||
if (ct == SNAKE_CELL_FOOD) {
|
||||
if (are_cells_full_(ctx)) {
|
||||
snake_initialize(ctx);
|
||||
return;
|
||||
}
|
||||
new_food_pos_(ctx);
|
||||
++ctx->inhibit_tail_step;
|
||||
++ctx->occupied_cells;
|
||||
}
|
||||
}
|
||||
|
||||
static Uint32 sdl_timer_callback_(void *payload, SDL_TimerID timer_id, Uint32 interval)
|
||||
{
|
||||
/* NOTE: snake_step is not called here directly for multithreaded concerns. */
|
||||
SDL_Event event;
|
||||
SDL_zero(event);
|
||||
event.type = SDL_EVENT_USER;
|
||||
SDL_PushEvent(&event);
|
||||
return interval;
|
||||
}
|
||||
|
||||
static int handle_key_event_(SnakeContext *ctx, SDL_Scancode key_code)
|
||||
{
|
||||
switch (key_code) {
|
||||
/* Quit. */
|
||||
case SDL_SCANCODE_ESCAPE:
|
||||
case SDL_SCANCODE_Q:
|
||||
return SDL_APP_SUCCESS;
|
||||
/* Restart the game as if the program was launched. */
|
||||
case SDL_SCANCODE_R:
|
||||
snake_initialize(ctx);
|
||||
break;
|
||||
/* Decide new direction of the snake. */
|
||||
case SDL_SCANCODE_RIGHT:
|
||||
snake_redir(ctx, SNAKE_DIR_RIGHT);
|
||||
break;
|
||||
case SDL_SCANCODE_UP:
|
||||
snake_redir(ctx, SNAKE_DIR_UP);
|
||||
break;
|
||||
case SDL_SCANCODE_LEFT:
|
||||
snake_redir(ctx, SNAKE_DIR_LEFT);
|
||||
break;
|
||||
case SDL_SCANCODE_DOWN:
|
||||
snake_redir(ctx, SNAKE_DIR_DOWN);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return SDL_APP_CONTINUE;
|
||||
}
|
||||
|
||||
SDL_AppResult SDL_AppIterate(void *appstate)
|
||||
{
|
||||
AppState *as;
|
||||
SnakeContext *ctx;
|
||||
SDL_FRect r;
|
||||
unsigned i;
|
||||
unsigned j;
|
||||
int ct;
|
||||
as = (AppState *)appstate;
|
||||
ctx = &as->snake_ctx;
|
||||
r.w = r.h = SNAKE_BLOCK_SIZE_IN_PIXELS;
|
||||
SDL_SetRenderDrawColor(as->renderer, 0, 0, 0, 255);
|
||||
SDL_RenderClear(as->renderer);
|
||||
for (i = 0; i < SNAKE_GAME_WIDTH; i++) {
|
||||
for (j = 0; j < SNAKE_GAME_HEIGHT; j++) {
|
||||
ct = snake_cell_at(ctx, i, j);
|
||||
if (ct == SNAKE_CELL_NOTHING)
|
||||
continue;
|
||||
set_rect_xy_(&r, i, j);
|
||||
if (ct == SNAKE_CELL_FOOD)
|
||||
SDL_SetRenderDrawColor(as->renderer, 80, 80, 255, 255);
|
||||
else /* body */
|
||||
SDL_SetRenderDrawColor(as->renderer, 0, 128, 0, 255);
|
||||
SDL_RenderFillRect(as->renderer, &r);
|
||||
}
|
||||
}
|
||||
SDL_SetRenderDrawColor(as->renderer, 255, 255, 0, 255); /*head*/
|
||||
set_rect_xy_(&r, ctx->head_xpos, ctx->head_ypos);
|
||||
SDL_RenderFillRect(as->renderer, &r);
|
||||
SDL_RenderPresent(as->renderer);
|
||||
return SDL_APP_CONTINUE;
|
||||
}
|
||||
|
||||
SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[])
|
||||
{
|
||||
if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER)) {
|
||||
return SDL_APP_FAILURE;
|
||||
}
|
||||
|
||||
AppState *as = SDL_calloc(1, sizeof(AppState));
|
||||
|
||||
*appstate = as;
|
||||
|
||||
if (!SDL_CreateWindowAndRenderer("examples/game/snake", SDL_WINDOW_WIDTH, SDL_WINDOW_HEIGHT, 0, &as->window, &as->renderer)) {
|
||||
return SDL_APP_FAILURE;
|
||||
}
|
||||
|
||||
snake_initialize(&as->snake_ctx);
|
||||
|
||||
as->step_timer = SDL_AddTimer(STEP_RATE_IN_MILLISECONDS, sdl_timer_callback_, NULL);
|
||||
if (as->step_timer == 0) {
|
||||
return SDL_APP_FAILURE;
|
||||
}
|
||||
|
||||
return SDL_APP_CONTINUE;
|
||||
}
|
||||
|
||||
SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event)
|
||||
{
|
||||
SnakeContext *ctx = &((AppState *)appstate)->snake_ctx;
|
||||
switch (event->type) {
|
||||
case SDL_EVENT_QUIT:
|
||||
return SDL_APP_SUCCESS;
|
||||
case SDL_EVENT_USER:
|
||||
snake_step(ctx);
|
||||
break;
|
||||
case SDL_EVENT_KEY_DOWN:
|
||||
return handle_key_event_(ctx, event->key.scancode);
|
||||
}
|
||||
return SDL_APP_CONTINUE;
|
||||
}
|
||||
|
||||
void SDL_AppQuit(void *appstate)
|
||||
{
|
||||
if (appstate != NULL) {
|
||||
AppState *as = (AppState *)appstate;
|
||||
SDL_RemoveTimer(as->step_timer);
|
||||
SDL_DestroyRenderer(as->renderer);
|
||||
SDL_DestroyWindow(as->window);
|
||||
SDL_free(as);
|
||||
}
|
||||
}
|
||||
77
examples/highlight-plugin.lua
Normal file
77
examples/highlight-plugin.lua
Normal file
@@ -0,0 +1,77 @@
|
||||
-- This code adapted from https://gitlab.com/saalen/highlight/-/wikis/Plug-Ins
|
||||
|
||||
-- first add a description of what the plug-in does
|
||||
Description="Add wiki.libsdl.org reference links to HTML, LaTeX or RTF output"
|
||||
|
||||
-- define the plugin categories (ie. supported output formats; languages)
|
||||
Categories = { "c", "c++" }
|
||||
|
||||
-- the syntaxUpdate function contains code related to syntax recognition
|
||||
function syntaxUpdate(desc)
|
||||
|
||||
-- if the current file is not C/C++ file we exit
|
||||
if desc~="C and C++" then
|
||||
return
|
||||
end
|
||||
|
||||
-- this function returns a qt-project reference link of the given token
|
||||
function getURL(token)
|
||||
-- generate the URL
|
||||
url='https://wiki.libsdl.org/SDL3/'.. token
|
||||
|
||||
-- embed the URL in a hyperlink according to the output format
|
||||
-- first HTML, then LaTeX and RTF
|
||||
if (HL_OUTPUT== HL_FORMAT_HTML or HL_OUTPUT == HL_FORMAT_XHTML) then
|
||||
return '<a class="hl" target="new" href="'
|
||||
.. url .. '">'.. token .. '</a>'
|
||||
elseif (HL_OUTPUT == HL_FORMAT_LATEX) then
|
||||
return '\\href{'..url..'}{'..token..'}'
|
||||
elseif (HL_OUTPUT == HL_FORMAT_RTF) then
|
||||
return '{{\\field{\\*\\fldinst HYPERLINK "'
|
||||
..url..'" }{\\fldrslt\\ul\\ulc0 '..token..'}}}'
|
||||
end
|
||||
end
|
||||
|
||||
-- the Decorate function will be invoked for every recognized token
|
||||
function Decorate(token, state)
|
||||
|
||||
-- we are only interested in keywords, preprocessor or default items
|
||||
if (state ~= HL_STANDARD and state ~= HL_KEYWORD and
|
||||
state ~=HL_PREPROC) then
|
||||
return
|
||||
end
|
||||
|
||||
-- SDL keywords start with SDL_
|
||||
-- if this pattern applies to the token, we return the URL
|
||||
-- if we return nothing, the token is outputted as is
|
||||
if string.find(token, "SDL_")==1 then
|
||||
return getURL(token)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
-- the themeUpdate function contains code related to the theme
|
||||
function themeUpdate(desc)
|
||||
-- the Injections table can be used to add style information to the theme
|
||||
|
||||
-- HTML: we add additional CSS style information to beautify hyperlinks,
|
||||
-- they should have the same color as their surrounding tags
|
||||
if (HL_OUTPUT == HL_FORMAT_HTML or HL_OUTPUT == HL_FORMAT_XHTML) then
|
||||
Injections[#Injections+1]=
|
||||
"a.hl, a.hl:visited {color:inherit;font-weight:inherit;text-decoration:none}"
|
||||
|
||||
-- LaTeX: hyperlinks require the hyperref package, so we add this here
|
||||
-- the colorlinks and pdfborderstyle options remove ugly boxes in the output
|
||||
elseif (HL_OUTPUT==HL_FORMAT_LATEX) then
|
||||
Injections[#Injections+1]=
|
||||
"\\usepackage[colorlinks=false, pdfborderstyle={/S/U/W 1}]{hyperref}"
|
||||
end
|
||||
end
|
||||
|
||||
-- let highlight load the chunks
|
||||
Plugins={
|
||||
{ Type="lang", Chunk=syntaxUpdate },
|
||||
{ Type="theme", Chunk=themeUpdate },
|
||||
}
|
||||
|
||||
3
examples/pen/01-drawing-lines/README.txt
Normal file
3
examples/pen/01-drawing-lines/README.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
You need something with a pen/stylus for this to work.
|
||||
|
||||
This takes pen input and draws lines. Lines are darker when you press harder.
|
||||
104
examples/pen/01-drawing-lines/drawing-lines.c
Normal file
104
examples/pen/01-drawing-lines/drawing-lines.c
Normal file
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* This example code reads pen/stylus input and draws lines. Darker lines
|
||||
* for harder pressure.
|
||||
*
|
||||
* SDL can track multiple pens, but for simplicity here, this assumes any
|
||||
* pen input we see was from one device.
|
||||
*
|
||||
* This code is public domain. Feel free to use it for any purpose!
|
||||
*/
|
||||
|
||||
#define SDL_MAIN_USE_CALLBACKS 1 /* use the callbacks instead of main() */
|
||||
#include <SDL3/SDL.h>
|
||||
#include <SDL3/SDL_main.h>
|
||||
|
||||
/* We will use this renderer to draw into this window every frame. */
|
||||
static SDL_Window *window = NULL;
|
||||
static SDL_Renderer *renderer = NULL;
|
||||
static SDL_Texture *render_target = NULL;
|
||||
static float pressure = 0.0f;
|
||||
static float previous_touch_x = -1.0f;
|
||||
static float previous_touch_y = -1.0f;
|
||||
|
||||
/* This function runs once at startup. */
|
||||
SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[])
|
||||
{
|
||||
if (!SDL_Init(SDL_INIT_VIDEO)) {
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Couldn't initialize SDL!", SDL_GetError(), NULL);
|
||||
return SDL_APP_FAILURE;
|
||||
}
|
||||
|
||||
if (!SDL_CreateWindowAndRenderer("examples/pen/drawing-lines", 640, 480, 0, &window, &renderer)) {
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Couldn't create window/renderer!", SDL_GetError(), NULL);
|
||||
return SDL_APP_FAILURE;
|
||||
}
|
||||
|
||||
/* we make a render target so we can draw lines to it and not have to record and redraw every pen stroke each frame.
|
||||
Instead rendering a frame for us is a single texture draw. */
|
||||
render_target = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, 640, 480);
|
||||
if (!render_target) {
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Couldn't create render target!", SDL_GetError(), NULL);
|
||||
return SDL_APP_FAILURE;
|
||||
}
|
||||
|
||||
/* just blank the render target to gray to start. */
|
||||
SDL_SetRenderTarget(renderer, render_target);
|
||||
SDL_SetRenderDrawColor(renderer, 100, 100, 100, 255);
|
||||
SDL_RenderClear(renderer);
|
||||
SDL_SetRenderTarget(renderer, NULL);
|
||||
SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);
|
||||
|
||||
return SDL_APP_CONTINUE; /* carry on with the program! */
|
||||
}
|
||||
|
||||
/* This function runs when a new event (mouse input, keypresses, etc) occurs. */
|
||||
SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event)
|
||||
{
|
||||
if (event->type == SDL_EVENT_QUIT) {
|
||||
return SDL_APP_SUCCESS; /* end the program, reporting success to the OS. */
|
||||
}
|
||||
|
||||
/* There are several events that track the specific stages of pen activity,
|
||||
but we're only going to look for motion and pressure, for simplicity. */
|
||||
if (event->type == SDL_EVENT_PEN_MOTION) {
|
||||
/* you can check for when the pen is touching, but if pressure > 0.0f, it's definitely touching! */
|
||||
if (pressure > 0.0f) {
|
||||
if (previous_touch_x >= 0.0f) { /* only draw if we're moving while touching */
|
||||
/* draw with the alpha set to the pressure, so you effectively get a fainter line for lighter presses. */
|
||||
SDL_SetRenderTarget(renderer, render_target);
|
||||
SDL_SetRenderDrawColorFloat(renderer, 0, 0, 0, pressure);
|
||||
SDL_RenderLine(renderer, previous_touch_x, previous_touch_y, event->pmotion.x, event->pmotion.y);
|
||||
}
|
||||
previous_touch_x = event->pmotion.x;
|
||||
previous_touch_y = event->pmotion.y;
|
||||
} else {
|
||||
previous_touch_x = previous_touch_y = -1.0f;
|
||||
}
|
||||
} else if (event->type == SDL_EVENT_PEN_AXIS) {
|
||||
if (event->paxis.axis == SDL_PEN_AXIS_PRESSURE) {
|
||||
pressure = event->paxis.value; /* remember new pressure for later draws. */
|
||||
}
|
||||
}
|
||||
|
||||
return SDL_APP_CONTINUE; /* carry on with the program! */
|
||||
}
|
||||
|
||||
/* This function runs once per frame, and is the heart of the program. */
|
||||
SDL_AppResult SDL_AppIterate(void *appstate)
|
||||
{
|
||||
/* make sure we're drawing to the window and not the render target */
|
||||
SDL_SetRenderTarget(renderer, NULL);
|
||||
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
|
||||
SDL_RenderClear(renderer); /* just in case. */
|
||||
SDL_RenderTexture(renderer, render_target, NULL, NULL);
|
||||
SDL_RenderPresent(renderer);
|
||||
return SDL_APP_CONTINUE; /* carry on with the program! */
|
||||
}
|
||||
|
||||
/* This function runs once at shutdown. */
|
||||
void SDL_AppQuit(void *appstate)
|
||||
{
|
||||
SDL_DestroyTexture(render_target);
|
||||
/* SDL will clean up the window/renderer for us. */
|
||||
}
|
||||
|
||||
3
examples/renderer/01-clear/README.txt
Normal file
3
examples/renderer/01-clear/README.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
This example code creates an SDL window and renderer, and then clears the
|
||||
window to a different color every frame, so you'll effectively get a window
|
||||
that's smoothly fading between colors.
|
||||
85
examples/renderer/01-clear/renderer-clear.c
Normal file
85
examples/renderer/01-clear/renderer-clear.c
Normal file
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* This example code creates an SDL window and renderer, and then clears the
|
||||
* window to a different color every frame, so you'll effectively get a window
|
||||
* that's smoothly fading between colors.
|
||||
*
|
||||
* This code is public domain. Feel free to use it for any purpose!
|
||||
*/
|
||||
|
||||
#define SDL_MAIN_USE_CALLBACKS 1 /* use the callbacks instead of main() */
|
||||
#include <SDL3/SDL.h>
|
||||
#include <SDL3/SDL_main.h>
|
||||
|
||||
|
||||
/* We will use this renderer to draw into this window every frame. */
|
||||
static SDL_Window *window = NULL;
|
||||
static SDL_Renderer *renderer = NULL;
|
||||
|
||||
/* the current red color we're clearing to. */
|
||||
static Uint8 red = 0;
|
||||
|
||||
/* When fading up, this is 1, when fading down, it's -1. */
|
||||
static int fade_direction = 1;
|
||||
|
||||
|
||||
/* This function runs once at startup. */
|
||||
SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[])
|
||||
{
|
||||
if (!SDL_Init(SDL_INIT_VIDEO)) {
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Couldn't initialize SDL!", SDL_GetError(), NULL);
|
||||
return SDL_APP_FAILURE;
|
||||
}
|
||||
|
||||
if (!SDL_CreateWindowAndRenderer("examples/renderer/clear", 640, 480, 0, &window, &renderer)) {
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Couldn't create window/renderer!", SDL_GetError(), NULL);
|
||||
return SDL_APP_FAILURE;
|
||||
}
|
||||
SDL_SetRenderVSync(renderer, 1); /* try to show frames at the monitor refresh rate. */
|
||||
return SDL_APP_CONTINUE; /* carry on with the program! */
|
||||
}
|
||||
|
||||
/* This function runs when a new event (mouse input, keypresses, etc) occurs. */
|
||||
SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event)
|
||||
{
|
||||
if (event->type == SDL_EVENT_QUIT) {
|
||||
return SDL_APP_SUCCESS; /* end the program, reporting success to the OS. */
|
||||
}
|
||||
return SDL_APP_CONTINUE; /* carry on with the program! */
|
||||
}
|
||||
|
||||
/* This function runs once per frame, and is the heart of the program. */
|
||||
SDL_AppResult SDL_AppIterate(void *appstate)
|
||||
{
|
||||
/* since we're always fading red, we leave green and blue at zero.
|
||||
alpha doesn't mean much here, so leave it at full (255, no transparency). */
|
||||
SDL_SetRenderDrawColor(renderer, red, 0, 0, 255);
|
||||
|
||||
/* clear the window to the draw color. */
|
||||
SDL_RenderClear(renderer);
|
||||
|
||||
/* put the newly-cleared rendering on the screen. */
|
||||
SDL_RenderPresent(renderer);
|
||||
|
||||
/* update the color for the next frame we will draw. */
|
||||
if (fade_direction > 0) {
|
||||
if (red == 255) {
|
||||
fade_direction = -1;
|
||||
} else {
|
||||
red++;
|
||||
}
|
||||
} else if (fade_direction < 0) {
|
||||
if (red == 0) {
|
||||
fade_direction = 1;
|
||||
} else {
|
||||
red--;
|
||||
}
|
||||
}
|
||||
return SDL_APP_CONTINUE; /* carry on with the program! */
|
||||
}
|
||||
|
||||
/* This function runs once at shutdown. */
|
||||
void SDL_AppQuit(void *appstate)
|
||||
{
|
||||
/* SDL will clean up the window/renderer for us. */
|
||||
}
|
||||
|
||||
3
examples/renderer/02-primitives/README.txt
Normal file
3
examples/renderer/02-primitives/README.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
This example creates an SDL window and renderer, and then draws some lines,
|
||||
rectangles and points to it every frame.
|
||||
|
||||
93
examples/renderer/02-primitives/renderer-primitives.c
Normal file
93
examples/renderer/02-primitives/renderer-primitives.c
Normal file
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* This example creates an SDL window and renderer, and then draws some lines,
|
||||
* rectangles and points to it every frame.
|
||||
*
|
||||
* This code is public domain. Feel free to use it for any purpose!
|
||||
*/
|
||||
|
||||
#define SDL_MAIN_USE_CALLBACKS 1 /* use the callbacks instead of main() */
|
||||
#include <SDL3/SDL.h>
|
||||
#include <SDL3/SDL_main.h>
|
||||
|
||||
/* We will use this renderer to draw into this window every frame. */
|
||||
static SDL_Window *window = NULL;
|
||||
static SDL_Renderer *renderer = NULL;
|
||||
static SDL_FPoint points[500];
|
||||
|
||||
/* This function runs once at startup. */
|
||||
SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[])
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!SDL_Init(SDL_INIT_VIDEO)) {
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Couldn't initialize SDL!", SDL_GetError(), NULL);
|
||||
return SDL_APP_FAILURE;
|
||||
}
|
||||
|
||||
if (!SDL_CreateWindowAndRenderer("examples/renderer/primitives", 640, 480, 0, &window, &renderer)) {
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Couldn't create window/renderer!", SDL_GetError(), NULL);
|
||||
return SDL_APP_FAILURE;
|
||||
}
|
||||
|
||||
/* set up some random points */
|
||||
for (i = 0; i < SDL_arraysize(points); i++) {
|
||||
points[i].x = (SDL_randf() * 440.0f) + 100.0f;
|
||||
points[i].y = (SDL_randf() * 280.0f) + 100.0f;
|
||||
}
|
||||
|
||||
return SDL_APP_CONTINUE; /* carry on with the program! */
|
||||
}
|
||||
|
||||
/* This function runs when a new event (mouse input, keypresses, etc) occurs. */
|
||||
SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event)
|
||||
{
|
||||
if (event->type == SDL_EVENT_QUIT) {
|
||||
return SDL_APP_SUCCESS; /* end the program, reporting success to the OS. */
|
||||
}
|
||||
return SDL_APP_CONTINUE; /* carry on with the program! */
|
||||
}
|
||||
|
||||
/* This function runs once per frame, and is the heart of the program. */
|
||||
SDL_AppResult SDL_AppIterate(void *appstate)
|
||||
{
|
||||
SDL_FRect rect;
|
||||
|
||||
/* as you can see from this, rendering draws over whatever was drawn before it. */
|
||||
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); /* black, full alpha */
|
||||
SDL_RenderClear(renderer); /* start with a blank canvas. */
|
||||
|
||||
/* draw a filled rectangle in the middle of the canvas. */
|
||||
SDL_SetRenderDrawColor(renderer, 0, 0, 255, 255); /* blue, full alpha */
|
||||
rect.x = rect.y = 100;
|
||||
rect.w = 440;
|
||||
rect.h = 280;
|
||||
SDL_RenderFillRect(renderer, &rect);
|
||||
|
||||
/* draw some points across the canvas. */
|
||||
SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255); /* red, full alpha */
|
||||
SDL_RenderPoints(renderer, points, SDL_arraysize(points));
|
||||
|
||||
/* draw a unfilled rectangle in-set a little bit. */
|
||||
SDL_SetRenderDrawColor(renderer, 0, 255, 0, 255); /* green, full alpha */
|
||||
rect.x += 30;
|
||||
rect.y += 30;
|
||||
rect.w -= 60;
|
||||
rect.h -= 60;
|
||||
SDL_RenderRect(renderer, &rect);
|
||||
|
||||
/* draw two lines in an X across the whole canvas. */
|
||||
SDL_SetRenderDrawColor(renderer, 255, 255, 0, 255); /* yellow, full alpha */
|
||||
SDL_RenderLine(renderer, 0, 0, 640, 480);
|
||||
SDL_RenderLine(renderer, 0, 480, 640, 0);
|
||||
|
||||
SDL_RenderPresent(renderer); /* put it all on the screen! */
|
||||
|
||||
return SDL_APP_CONTINUE; /* carry on with the program! */
|
||||
}
|
||||
|
||||
/* This function runs once at shutdown. */
|
||||
void SDL_AppQuit(void *appstate)
|
||||
{
|
||||
/* SDL will clean up the window/renderer for us. */
|
||||
}
|
||||
|
||||
51
examples/template.c
Normal file
51
examples/template.c
Normal file
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* This example code $WHAT_IT_DOES.
|
||||
*
|
||||
* This code is public domain. Feel free to use it for any purpose!
|
||||
*/
|
||||
|
||||
#define SDL_MAIN_USE_CALLBACKS 1 /* use the callbacks instead of main() */
|
||||
#include <SDL3/SDL.h>
|
||||
#include <SDL3/SDL_main.h>
|
||||
|
||||
/* We will use this renderer to draw into this window every frame. */
|
||||
static SDL_Window *window = NULL;
|
||||
static SDL_Renderer *renderer = NULL;
|
||||
|
||||
|
||||
/* This function runs once at startup. */
|
||||
SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[])
|
||||
{
|
||||
if (!SDL_Init(SDL_INIT_VIDEO)) {
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Couldn't initialize SDL!", SDL_GetError(), NULL);
|
||||
return SDL_APP_FAILURE;
|
||||
}
|
||||
|
||||
if (!SDL_CreateWindowAndRenderer("examples/CATEGORY/NAME", 640, 480, 0, &window, &renderer)) {
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Couldn't create window/renderer!", SDL_GetError(), NULL);
|
||||
return SDL_APP_FAILURE;
|
||||
}
|
||||
return SDL_APP_CONTINUE; /* carry on with the program! */
|
||||
}
|
||||
|
||||
/* This function runs when a new event (mouse input, keypresses, etc) occurs. */
|
||||
SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event)
|
||||
{
|
||||
if (event->type == SDL_EVENT_QUIT) {
|
||||
return SDL_APP_SUCCESS; /* end the program, reporting success to the OS. */
|
||||
}
|
||||
return SDL_APP_CONTINUE; /* carry on with the program! */
|
||||
}
|
||||
|
||||
/* This function runs once per frame, and is the heart of the program. */
|
||||
SDL_AppResult SDL_AppIterate(void *appstate)
|
||||
{
|
||||
return SDL_APP_CONTINUE; /* carry on with the program! */
|
||||
}
|
||||
|
||||
/* This function runs once at shutdown. */
|
||||
void SDL_AppQuit(void *appstate)
|
||||
{
|
||||
/* SDL will clean up the window/renderer for us. */
|
||||
}
|
||||
|
||||
218
examples/template.html
Normal file
218
examples/template.html
Normal file
@@ -0,0 +1,218 @@
|
||||
<!doctype html>
|
||||
<html lang="en-us">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<title>@project_name@ Example: @category_name@/@example_name@</title>
|
||||
<style>
|
||||
html, body {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
font-family: 'Liberation Sans', sans-serif;
|
||||
}
|
||||
|
||||
.canvas-container {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
background: black;
|
||||
}
|
||||
|
||||
#canvas {
|
||||
box-shadow: 0 0 0.5rem #7787;
|
||||
}
|
||||
|
||||
#output-container {
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
|
||||
background-color: black;
|
||||
border: none;
|
||||
border-top: 1px solid #778;
|
||||
margin: 0;
|
||||
padding: 1rem;
|
||||
|
||||
transition: top 0.25s;
|
||||
}
|
||||
|
||||
#output-container::before {
|
||||
position: absolute;
|
||||
bottom: 100%;
|
||||
right: 1rem;
|
||||
|
||||
content: 'Console';
|
||||
font-size: 1.5rem;
|
||||
color: white;
|
||||
background: black;
|
||||
border: 1px solid #778;
|
||||
border-bottom: none;
|
||||
padding: 0.75rem 1.5rem;
|
||||
border-radius: 0.5rem 0.5rem 0 0;
|
||||
}
|
||||
|
||||
#output-container:hover,
|
||||
#output-container:focus-within {
|
||||
top: 50%;
|
||||
}
|
||||
|
||||
#output-container:focus-within {
|
||||
border-top: 2px solid orange;
|
||||
}
|
||||
|
||||
#output-container:focus-within::before {
|
||||
border: 2px solid orange;
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
#output {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
|
||||
border: none;
|
||||
background: black;
|
||||
color: white;
|
||||
outline: none;
|
||||
resize: none;
|
||||
|
||||
font-family: 'Lucida Console', Monaco, monospace;
|
||||
}
|
||||
|
||||
#source-code {
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
|
||||
background: #e0eaee;
|
||||
padding: 1rem;
|
||||
|
||||
transition: top 0.25s;
|
||||
}
|
||||
|
||||
#source-code::before {
|
||||
position: absolute;
|
||||
bottom: 100%;
|
||||
left: 1rem;
|
||||
|
||||
content: 'Source code';
|
||||
font-size: 1.5rem;
|
||||
background: linear-gradient(to bottom, #789, #e0eaee);
|
||||
padding: 0.75rem 1.5rem;
|
||||
border-radius: 0.5rem 0.5rem 0 0;
|
||||
}
|
||||
|
||||
#source-code:hover,
|
||||
#source-code:focus-within {
|
||||
top: 50%;
|
||||
}
|
||||
|
||||
#source-code:focus-within {
|
||||
border-top: 2px solid orange;
|
||||
}
|
||||
|
||||
#source-code:focus-within::before {
|
||||
border: 2px solid orange;
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
#source-code-contents {
|
||||
height: 100%;
|
||||
overflow: scroll;
|
||||
}
|
||||
|
||||
#example-description {
|
||||
color: white;
|
||||
text-align: center;
|
||||
position: relative; /* required for proper positioning */
|
||||
}
|
||||
</style>
|
||||
<link rel="stylesheet" type="text/css" href="highlight.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="canvas-container">
|
||||
<canvas id="canvas" oncontextmenu="event.preventDefault()" tabindex="-1"></canvas>
|
||||
</div>
|
||||
<div id="example-description">
|
||||
@description@
|
||||
</div>
|
||||
<div id="output-container">
|
||||
<textarea id="output" rows="8" spellcheck="false" readonly></textarea>
|
||||
</div>
|
||||
|
||||
<div id="source-code" tabindex="1">
|
||||
<div id="source-code-contents">@htmlified_source_code@</div>
|
||||
</div>
|
||||
|
||||
<script type='text/javascript'>
|
||||
var Module = {
|
||||
preRun: [],
|
||||
postRun: [],
|
||||
print: (function() {
|
||||
var element = document.getElementById('output');
|
||||
if (element) element.value = ''; // clear browser cache
|
||||
return function(text) {
|
||||
var elem = document.getElementById('output-container');
|
||||
if (elem.style['top'] == '') {
|
||||
elem.style['top'] = '50%';
|
||||
setTimeout(function() { elem.style['top'] = ''; }, 3000);
|
||||
}
|
||||
|
||||
if (arguments.length > 1) text = Array.prototype.slice.call(arguments).join(' ');
|
||||
// These replacements are necessary if you render to raw HTML
|
||||
//text = text.replace(/&/g, "&");
|
||||
//text = text.replace(/</g, "<");
|
||||
//text = text.replace(/>/g, ">");
|
||||
//text = text.replace('\n', '<br>', 'g');
|
||||
console.log(text);
|
||||
if (element) {
|
||||
element.value += text + "\n";
|
||||
element.scrollTop = element.scrollHeight; // focus on bottom
|
||||
}
|
||||
};
|
||||
})(),
|
||||
printErr: function(text) { Module.print(text) },
|
||||
canvas: (() => {
|
||||
var canvas = document.getElementById('canvas');
|
||||
|
||||
// As a default initial behavior, pop up an alert when webgl context is lost. To make your
|
||||
// application robust, you may want to override this behavior before shipping!
|
||||
// See http://www.khronos.org/registry/webgl/specs/latest/1.0/#5.15.2
|
||||
canvas.addEventListener("webglcontextlost", (e) => { alert('WebGL context lost. You will need to reload the page.'); e.preventDefault(); }, false);
|
||||
|
||||
return canvas;
|
||||
})(),
|
||||
setStatus: (text) => {},
|
||||
totalDependencies: 0,
|
||||
monitorRunDependencies: (left) => {
|
||||
this.totalDependencies = Math.max(this.totalDependencies, left);
|
||||
Module.setStatus(left ? 'Preparing... (' + (this.totalDependencies-left) + '/' + this.totalDependencies + ')' : 'All downloads complete.');
|
||||
}
|
||||
};
|
||||
Module.setStatus('Downloading...');
|
||||
window.onerror = (event) => {
|
||||
// TODO: do not warn on ok events like simulating an infinite loop or exitStatus
|
||||
Module.setStatus('Exception thrown, see JavaScript console');
|
||||
Module.setStatus = (text) => {
|
||||
if (text) console.error('[post-exception status] ' + text);
|
||||
};
|
||||
};
|
||||
</script>
|
||||
<script async type="text/javascript" src="@javascript_file@"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user