chore(build): use SDL3

This commit is contained in:
phaneron 2025-04-12 04:38:19 -04:00
parent 9d04a35d87
commit b3c0734a9e
3286 changed files with 866354 additions and 554996 deletions

View file

@ -0,0 +1,382 @@
#
# 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()
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}"
)
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(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}"
)
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)
elseif(EMSCRIPTEN)
set_property(TARGET ${TARGET} PROPERTY SUFFIX ".html")
target_link_options(${TARGET} PRIVATE -sALLOW_MEMORY_GROWTH=1)
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/clear.c)
add_sdl_example_executable(renderer-primitives SOURCES renderer/02-primitives/primitives.c)
add_sdl_example_executable(renderer-lines SOURCES renderer/03-lines/lines.c)
add_sdl_example_executable(renderer-points SOURCES renderer/04-points/points.c)
add_sdl_example_executable(renderer-rectangles SOURCES renderer/05-rectangles/rectangles.c)
add_sdl_example_executable(renderer-textures SOURCES renderer/06-textures/textures.c DATAFILES ${CMAKE_CURRENT_SOURCE_DIR}/../test/sample.bmp)
add_sdl_example_executable(renderer-streaming-textures SOURCES renderer/07-streaming-textures/streaming-textures.c)
add_sdl_example_executable(renderer-rotating-textures SOURCES renderer/08-rotating-textures/rotating-textures.c DATAFILES ${CMAKE_CURRENT_SOURCE_DIR}/../test/sample.bmp)
add_sdl_example_executable(renderer-scaling-textures SOURCES renderer/09-scaling-textures/scaling-textures.c DATAFILES ${CMAKE_CURRENT_SOURCE_DIR}/../test/sample.bmp)
add_sdl_example_executable(renderer-geometry SOURCES renderer/10-geometry/geometry.c DATAFILES ${CMAKE_CURRENT_SOURCE_DIR}/../test/sample.bmp)
add_sdl_example_executable(renderer-color-mods SOURCES renderer/11-color-mods/color-mods.c DATAFILES ${CMAKE_CURRENT_SOURCE_DIR}/../test/sample.bmp)
add_sdl_example_executable(renderer-viewport SOURCES renderer/14-viewport/viewport.c DATAFILES ${CMAKE_CURRENT_SOURCE_DIR}/../test/sample.bmp)
add_sdl_example_executable(renderer-cliprect SOURCES renderer/15-cliprect/cliprect.c DATAFILES ${CMAKE_CURRENT_SOURCE_DIR}/../test/sample.bmp)
add_sdl_example_executable(renderer-read-pixels SOURCES renderer/17-read-pixels/read-pixels.c DATAFILES ${CMAKE_CURRENT_SOURCE_DIR}/../test/sample.bmp)
add_sdl_example_executable(renderer-debug-text SOURCES renderer/18-debug-text/debug-text.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(audio-multiple-streams SOURCES audio/04-multiple-streams/multiple-streams.c DATAFILES ${CMAKE_CURRENT_SOURCE_DIR}/../test/sample.wav ${CMAKE_CURRENT_SOURCE_DIR}/../test/sword.wav)
add_sdl_example_executable(input-joystick-polling SOURCES input/01-joystick-polling/joystick-polling.c)
add_sdl_example_executable(input-joystick-events SOURCES input/02-joystick-events/joystick-events.c)
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(asyncio-load-bitmaps SOURCES asyncio/01-load-bitmaps/load-bitmaps.c DATAFILES ${CMAKE_CURRENT_SOURCE_DIR}/../test/sample.bmp ${CMAKE_CURRENT_SOURCE_DIR}/../test/gamepad_front.bmp ${CMAKE_CURRENT_SOURCE_DIR}/../test/speaker.bmp ${CMAKE_CURRENT_SOURCE_DIR}/../test/icon2x.bmp)
add_sdl_example_executable(demo-snake SOURCES demo/01-snake/snake.c)
add_sdl_example_executable(demo-woodeneye-008 SOURCES demo/02-woodeneye-008/woodeneye-008.c)
add_sdl_example_executable(demo-infinite-monkeys SOURCES demo/03-infinite-monkeys/infinite-monkeys.c)
add_sdl_example_executable(demo-bytepusher SOURCES demo/04-bytepusher/bytepusher.c)
# When you add an example, remember to add the Visual Studio project as well:
# - Add a new example in examples/
# - Run python VisualC/examples/generate.py
# - Take note of the newly generated .vcxproj files
# - Modify the .vcxproj files if necessary (adding content such as BMP or WAV files)
# - Open VisualC/SDL.sln in Visual Studio or JetBrains Rider
# - Locate the appropriate folder in the Solution Explorer
# - Add the newly generated projects: Right click -> Add -> Existing project...
# - Test if they work
# - Save the SDL.sln solution
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
OUTPUT_DIR $<TARGET_FILE_DIR:${APP}>/sdl-${APP}
)
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
vendor/sdl-3.2.10/examples/README.md vendored Normal file
View 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.

View file

@ -0,0 +1,6 @@
This example code loads a few bitmap files from disk using the asynchronous
i/o, and then draws it to the window. It uses a task group to watch multiple
reads and deal with them in whatever order they finish.
Note that for a single tiny file like this, you'd probably not want to bother
with async i/o in real life, but this is just an example of how to do it.

View file

@ -0,0 +1,125 @@
/*
* This example code loads a bitmap with asynchronous i/o and renders it.
*
* 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_AsyncIOQueue *queue = NULL;
#define TOTAL_TEXTURES 4
static const char * const bmps[TOTAL_TEXTURES] = { "sample.bmp", "gamepad_front.bmp", "speaker.bmp", "icon2x.bmp" };
static SDL_Texture *textures[TOTAL_TEXTURES];
static const SDL_FRect texture_rects[TOTAL_TEXTURES] = {
{ 116, 156, 408, 167 },
{ 20, 200, 96, 60 },
{ 525, 180, 96, 96 },
{ 288, 375, 64, 64 }
};
/* 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/asyncio/load-bitmaps", 640, 480, 0, &window, &renderer)) {
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Couldn't create window/renderer!", SDL_GetError(), NULL);
return SDL_APP_FAILURE;
}
queue = SDL_CreateAsyncIOQueue();
if (!queue) {
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Couldn't create async i/o queue!", SDL_GetError(), NULL);
return SDL_APP_FAILURE;
}
/* Load some .bmp files asynchronously from wherever the app is being run from, put them in the same queue. */
for (i = 0; i < SDL_arraysize(bmps); i++) {
char *path = NULL;
SDL_asprintf(&path, "%s%s", SDL_GetBasePath(), bmps[i]); /* allocate a string of the full file path */
/* you _should) check for failure, but we'll just go on without files here. */
SDL_LoadFileAsync(path, queue, (void *) bmps[i]); /* attach the filename as app-specific data, so we can see it later. */
SDL_free(path);
}
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_AsyncIOOutcome outcome;
int i;
if (SDL_GetAsyncIOResult(queue, &outcome)) { /* a .bmp file load has finished? */
if (outcome.result == SDL_ASYNCIO_COMPLETE) {
/* this might be _any_ of the bmps; they might finish loading in any order. */
for (i = 0; i < SDL_arraysize(bmps); i++) {
/* this doesn't need a strcmp because we gave the pointer from this array to SDL_LoadFileAsync */
if (outcome.userdata == bmps[i]) {
break;
}
}
if (i < SDL_arraysize(bmps)) { /* (just in case.) */
SDL_Surface *surface = SDL_LoadBMP_IO(SDL_IOFromConstMem(outcome.buffer, (size_t) outcome.bytes_transferred), true);
if (surface) { /* the renderer is not multithreaded, so create the texture here once the data loads. */
textures[i] = SDL_CreateTextureFromSurface(renderer, surface);
if (!textures[i]) {
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Couldn't create texture!", SDL_GetError(), NULL);
return SDL_APP_FAILURE;
}
SDL_DestroySurface(surface);
}
}
}
SDL_free(outcome.buffer);
}
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
SDL_RenderClear(renderer);
for (i = 0; i < SDL_arraysize(textures); i++) {
SDL_RenderTexture(renderer, textures[i], NULL, &texture_rects[i]);
}
SDL_RenderPresent(renderer);
return SDL_APP_CONTINUE; /* carry on with the program! */
}
/* This function runs once at shutdown. */
void SDL_AppQuit(void *appstate, SDL_AppResult result)
{
int i;
SDL_DestroyAsyncIOQueue(queue);
for (i = 0; i < SDL_arraysize(textures); i++) {
SDL_DestroyTexture(textures[i]);
}
/* SDL will clean up the window/renderer for us. */
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

View file

@ -0,0 +1 @@
Asynchronous I/O

View 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 a 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.

View file

@ -0,0 +1,103 @@
/*
* This example code creates a 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 current_sine_sample = 0;
/* This function runs once at startup. */
SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[])
{
SDL_AudioSpec spec;
SDL_SetAppMetadata("Example Audio Simple Playback", "1.0", "com.example.audio-simple-playback");
if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO)) {
SDL_Log("Couldn't initialize SDL: %s", SDL_GetError());
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_Log("Couldn't create window/renderer: %s", SDL_GetError());
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_Log("Couldn't create audio stream: %s", SDL_GetError());
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_GetAudioStreamQueued(stream) < minimum_audio) {
static float samples[512]; /* this will feed 512 samples each frame until we get to our maximum. */
int i;
/* generate a 440Hz pure tone */
for (i = 0; i < SDL_arraysize(samples); i++) {
const int freq = 440;
const float phase = current_sine_sample * freq / 8000.0f;
samples[i] = SDL_sinf(phase * 2 * SDL_PI_F);
current_sine_sample++;
}
/* wrapping around to avoid floating-point errors */
current_sine_sample %= 8000;
/* 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_AppResult result)
{
/* SDL will clean up the window/renderer for us. */
}

View 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 a 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.

View file

@ -0,0 +1,115 @@
/*
* This example code creates a 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 current_sine_sample = 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;
/* generate a 440Hz pure tone */
for (i = 0; i < total; i++) {
const int freq = 440;
const float phase = current_sine_sample * freq / 8000.0f;
samples[i] = SDL_sinf(phase * 2 * SDL_PI_F);
current_sine_sample++;
}
/* wrapping around to avoid floating-point errors */
current_sine_sample %= 8000;
/* 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;
SDL_SetAppMetadata("Example Simple Audio Playback Callback", "1.0", "com.example.audio-simple-playback-callback");
if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO)) {
SDL_Log("Couldn't initialize SDL: %s", SDL_GetError());
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_Log("Couldn't create window/renderer: %s", SDL_GetError());
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_Log("Couldn't create audio stream: %s", SDL_GetError());
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_AppResult result)
{
/* SDL will clean up the window/renderer for us. */
}

View 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 a simple audio stream for playing sound, and
loads a .wav file that is pushed through the stream in a loop.

View file

@ -0,0 +1,103 @@
/*
* This example code creates a 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;
SDL_SetAppMetadata("Example Audio Load Wave", "1.0", "com.example.audio-load-wav");
if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO)) {
SDL_Log("Couldn't initialize SDL: %s", SDL_GetError());
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_Log("Couldn't create window/renderer: %s", SDL_GetError());
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_Log("Couldn't load .wav file: %s", SDL_GetError());
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_Log("Couldn't create audio stream: %s", SDL_GetError());
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_GetAudioStreamQueued(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_AppResult result)
{
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. */
}

View 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 loads two .wav files, puts them an audio streams and binds
them for playback, repeating both sounds on loop. This shows several streams
mixing into a single playback device.

View file

@ -0,0 +1,135 @@
/*
* This example code loads two .wav files, puts them an audio streams and
* binds them for playback, repeating both sounds on loop. This shows several
* streams mixing into a single playback 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_AudioDeviceID audio_device = 0;
/* things that are playing sound (the audiostream itself, plus the original data, so we can refill to loop. */
typedef struct Sound {
Uint8 *wav_data;
Uint32 wav_data_len;
SDL_AudioStream *stream;
} Sound;
static Sound sounds[2];
static bool init_sound(const char *fname, Sound *sound)
{
bool retval = false;
SDL_AudioSpec spec;
char *wav_path = NULL;
/* Load the .wav files from wherever the app is being run from. */
SDL_asprintf(&wav_path, "%s%s", SDL_GetBasePath(), fname); /* allocate a string of the full file path */
if (!SDL_LoadWAV(wav_path, &spec, &sound->wav_data, &sound->wav_data_len)) {
SDL_Log("Couldn't load .wav file: %s", SDL_GetError());
return false;
}
/* Create an audio stream. Set the source format to the wav's format (what
we'll input), leave the dest format NULL here (it'll change to what the
device wants once we bind it). */
sound->stream = SDL_CreateAudioStream(&spec, NULL);
if (!sound->stream) {
SDL_Log("Couldn't create audio stream: %s", SDL_GetError());
} else if (!SDL_BindAudioStream(audio_device, sound->stream)) { /* once bound, it'll start playing when there is data available! */
SDL_Log("Failed to bind '%s' stream to device: %s", fname, SDL_GetError());
} else {
retval = true; /* success! */
}
SDL_free(wav_path); /* done with this string. */
return retval;
}
/* This function runs once at startup. */
SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[])
{
SDL_SetAppMetadata("Example Audio Multiple Streams", "1.0", "com.example.audio-multiple-streams");
if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO)) {
SDL_Log("Couldn't initialize SDL: %s", SDL_GetError());
return SDL_APP_FAILURE;
}
if (!SDL_CreateWindowAndRenderer("examples/audio/multiple-streams", 640, 480, 0, &window, &renderer)) {
SDL_Log("Couldn't create window/renderer: %s", SDL_GetError());
return SDL_APP_FAILURE;
}
/* open the default audio device in whatever format it prefers; our audio streams will adjust to it. */
audio_device = SDL_OpenAudioDevice(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, NULL);
if (audio_device == 0) {
SDL_Log("Couldn't open audio device: %s", SDL_GetError());
return SDL_APP_FAILURE;
}
if (!init_sound("sample.wav", &sounds[0])) {
return SDL_APP_FAILURE;
} else if (!init_sound("sword.wav", &sounds[1])) {
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)
{
int i;
for (i = 0; i < SDL_arraysize(sounds); i++) {
/* If less than a full copy of the audio is queued for playback, put another copy in there.
This is overkill, but easy when lots of RAM is cheap. One could be more careful and
queue less at a time, as long as the stream doesn't run dry. */
if (SDL_GetAudioStreamQueued(sounds[i].stream) < ((int) sounds[i].wav_data_len)) {
SDL_PutAudioStreamData(sounds[i].stream, sounds[i].wav_data, (int) sounds[i].wav_data_len);
}
}
/* just blank the screen. */
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
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_AppResult result)
{
int i;
SDL_CloseAudioDevice(audio_device);
for (i = 0; i < SDL_arraysize(sounds); i++) {
if (sounds[i].stream) {
SDL_DestroyAudioStream(sounds[i].stream);
}
SDL_free(sounds[i].wav_data);
}
/* SDL will clean up the window/renderer for us. */
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 172 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View file

@ -0,0 +1 @@
This reads from a webcam and draws frames of video to the screen.

Binary file not shown.

After

Width:  |  Height:  |  Size: 339 KiB

View file

@ -0,0 +1,113 @@
/*
* 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;
SDL_SetAppMetadata("Example Camera Read and Draw", "1.0", "com.example.camera-read-and-draw");
if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_CAMERA)) {
SDL_Log("Couldn't initialize SDL: %s", SDL_GetError());
return SDL_APP_FAILURE;
}
if (!SDL_CreateWindowAndRenderer("examples/camera/read-and-draw", 640, 480, 0, &window, &renderer)) {
SDL_Log("Couldn't create window/renderer: %s", SDL_GetError());
return SDL_APP_FAILURE;
}
devices = SDL_GetCameras(&devcount);
if (devices == NULL) {
SDL_Log("Couldn't enumerate camera devices: %s", SDL_GetError());
return SDL_APP_FAILURE;
} else if (devcount == 0) {
SDL_Log("Couldn't find any camera devices! Please connect a camera and try again.");
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_Log("Couldn't open camera: %s", SDL_GetError());
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!");
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, &timestampNS);
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, SDL_ALPHA_OPAQUE);
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_AppResult result)
{
SDL_CloseCamera(camera);
SDL_DestroyTexture(texture);
/* SDL will clean up the window/renderer for us. */
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 234 KiB

View file

@ -0,0 +1,13 @@
# Blank lines and lines that start with '#' in this file are ignored.
# Categories, by directory name, go in here, in the order they should be
# listed on the main page. If this file is missing, it'll assume any
# subdirectory is a category and sort them alphabetically.
renderer
input
audio
camera
asyncio
pen
demo

View file

@ -0,0 +1 @@
A complete game of Snake, written in SDL.

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

View file

@ -0,0 +1,350 @@
/*
* Logic implementation of the Snake game. It is designed to efficiently
* represent the state of the game in memory.
*
* 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;
SnakeContext snake_ctx;
Uint64 last_step;
} 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 (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 SDL_AppResult 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 = (AppState *)appstate;
SnakeContext *ctx = &as->snake_ctx;
const Uint64 now = SDL_GetTicks();
SDL_FRect r;
unsigned i;
unsigned j;
int ct;
// run game logic if we're at or past the time to run it.
// if we're _really_ behind the time to run it, run it
// several times.
while ((now - as->last_step) >= STEP_RATE_IN_MILLISECONDS) {
snake_step(ctx);
as->last_step += STEP_RATE_IN_MILLISECONDS;
}
r.w = r.h = SNAKE_BLOCK_SIZE_IN_PIXELS;
SDL_SetRenderDrawColor(as->renderer, 0, 0, 0, SDL_ALPHA_OPAQUE);
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, SDL_ALPHA_OPAQUE);
else /* body */
SDL_SetRenderDrawColor(as->renderer, 0, 128, 0, SDL_ALPHA_OPAQUE);
SDL_RenderFillRect(as->renderer, &r);
}
}
SDL_SetRenderDrawColor(as->renderer, 255, 255, 0, SDL_ALPHA_OPAQUE); /*head*/
set_rect_xy_(&r, ctx->head_xpos, ctx->head_ypos);
SDL_RenderFillRect(as->renderer, &r);
SDL_RenderPresent(as->renderer);
return SDL_APP_CONTINUE;
}
static const struct
{
const char *key;
const char *value;
} extended_metadata[] =
{
{ SDL_PROP_APP_METADATA_URL_STRING, "https://examples.libsdl.org/SDL3/demo/01-snake/" },
{ SDL_PROP_APP_METADATA_CREATOR_STRING, "SDL team" },
{ SDL_PROP_APP_METADATA_COPYRIGHT_STRING, "Placed in the public domain" },
{ SDL_PROP_APP_METADATA_TYPE_STRING, "game" }
};
SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[])
{
size_t i;
if (!SDL_SetAppMetadata("Example Snake game", "1.0", "com.example.Snake")) {
return SDL_APP_FAILURE;
}
for (i = 0; i < SDL_arraysize(extended_metadata); i++) {
if (!SDL_SetAppMetadataProperty(extended_metadata[i].key, extended_metadata[i].value)) {
return SDL_APP_FAILURE;
}
}
if (!SDL_Init(SDL_INIT_VIDEO)) {
return SDL_APP_FAILURE;
}
AppState *as = (AppState *)SDL_calloc(1, sizeof(AppState));
if (!as) {
return SDL_APP_FAILURE;
}
*appstate = as;
if (!SDL_CreateWindowAndRenderer("examples/demo/snake", SDL_WINDOW_WIDTH, SDL_WINDOW_HEIGHT, 0, &as->window, &as->renderer)) {
return SDL_APP_FAILURE;
}
snake_initialize(&as->snake_ctx);
as->last_step = SDL_GetTicks();
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_KEY_DOWN:
return handle_key_event_(ctx, event->key.scancode);
}
return SDL_APP_CONTINUE;
}
void SDL_AppQuit(void *appstate, SDL_AppResult result)
{
if (appstate != NULL) {
AppState *as = (AppState *)appstate;
SDL_DestroyRenderer(as->renderer);
SDL_DestroyWindow(as->window);
SDL_free(as);
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

View file

@ -0,0 +1 @@
Minimal splitscreen FPS with multiple mouse and keyboards, drawn with the 2D Render API.

Binary file not shown.

After

Width:  |  Height:  |  Size: 457 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

View file

@ -0,0 +1,480 @@
/*
* 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 MAP_BOX_SCALE 16
#define MAP_BOX_EDGES_LEN (12 + MAP_BOX_SCALE * 2)
#define MAX_PLAYER_COUNT 4
#define CIRCLE_DRAW_SIDES 32
#define CIRCLE_DRAW_SIDES_LEN (CIRCLE_DRAW_SIDES + 1)
typedef struct {
SDL_MouseID mouse;
SDL_KeyboardID keyboard;
double pos[3];
double vel[3];
unsigned int yaw;
int pitch;
float radius, height;
unsigned char color[3];
unsigned char wasd;
} Player;
typedef struct {
SDL_Window *window;
SDL_Renderer *renderer;
int player_count;
Player players[MAX_PLAYER_COUNT];
float edges[MAP_BOX_EDGES_LEN][6];
} AppState;
static const struct {
const char *key;
const char *value;
} extended_metadata[] = {
{ SDL_PROP_APP_METADATA_URL_STRING, "https://examples.libsdl.org/SDL3/demo/02-woodeneye-008/" },
{ SDL_PROP_APP_METADATA_CREATOR_STRING, "SDL team" },
{ SDL_PROP_APP_METADATA_COPYRIGHT_STRING, "Placed in the public domain" },
{ SDL_PROP_APP_METADATA_TYPE_STRING, "game" }
};
static int whoseMouse(SDL_MouseID mouse, const Player players[], int players_len)
{
int i;
for (i = 0; i < players_len; i++) {
if (players[i].mouse == mouse) return i;
}
return -1;
}
static int whoseKeyboard(SDL_KeyboardID keyboard, const Player players[], int players_len)
{
int i;
for (i = 0; i < players_len; i++) {
if (players[i].keyboard == keyboard) return i;
}
return -1;
}
static void shoot(int shooter, Player players[], int players_len)
{
int i, j;
double x0 = players[shooter].pos[0];
double y0 = players[shooter].pos[1];
double z0 = players[shooter].pos[2];
double bin_rad = SDL_PI_D / 2147483648.0;
double yaw_rad = bin_rad * players[shooter].yaw;
double pitch_rad = bin_rad * players[shooter].pitch;
double cos_yaw = SDL_cos( yaw_rad);
double sin_yaw = SDL_sin( yaw_rad);
double cos_pitch = SDL_cos(pitch_rad);
double sin_pitch = SDL_sin(pitch_rad);
double vx = -sin_yaw*cos_pitch;
double vy = sin_pitch;
double vz = -cos_yaw*cos_pitch;
for (i = 0; i < players_len; i++) {
if (i == shooter) continue;
Player *target = &(players[i]);
int hit = 0;
for (j = 0; j < 2; j++) {
double r = target->radius;
double h = target->height;
double dx = target->pos[0] - x0;
double dy = target->pos[1] - y0 + (j == 0 ? 0 : r - h);
double dz = target->pos[2] - z0;
double vd = vx*dx + vy*dy + vz*dz;
double dd = dx*dx + dy*dy + dz*dz;
double vv = vx*vx + vy*vy + vz*vz;
double rr = r * r;
if (vd < 0) continue;
if (vd * vd >= vv * (dd - rr)) hit += 1;
}
if (hit) {
target->pos[0] = (double)(MAP_BOX_SCALE * (SDL_rand(256) - 128)) / 256;
target->pos[1] = (double)(MAP_BOX_SCALE * (SDL_rand(256) - 128)) / 256;
target->pos[2] = (double)(MAP_BOX_SCALE * (SDL_rand(256) - 128)) / 256;
}
}
}
static void update(Player *players, int players_len, Uint64 dt_ns)
{
int i;
for (i = 0; i < players_len; i++) {
Player *player = &players[i];
double rate = 6.0;
double time = (double)dt_ns * 1e-9;
double drag = SDL_exp(-time * rate);
double diff = 1.0 - drag;
double mult = 60.0;
double grav = 25.0;
double yaw = (double)player->yaw;
double rad = yaw * SDL_PI_D / 2147483648.0;
double cos = SDL_cos(rad);
double sin = SDL_sin(rad);
unsigned char wasd = player->wasd;
double dirX = (wasd & 8 ? 1.0 : 0.0) - (wasd & 2 ? 1.0 : 0.0);
double dirZ = (wasd & 4 ? 1.0 : 0.0) - (wasd & 1 ? 1.0 : 0.0);
double norm = dirX * dirX + dirZ * dirZ;
double accX = mult * (norm == 0 ? 0 : ( cos*dirX + sin*dirZ) / SDL_sqrt(norm));
double accZ = mult * (norm == 0 ? 0 : (-sin*dirX + cos*dirZ) / SDL_sqrt(norm));
double velX = player->vel[0];
double velY = player->vel[1];
double velZ = player->vel[2];
player->vel[0] -= velX * diff;
player->vel[1] -= grav * time;
player->vel[2] -= velZ * diff;
player->vel[0] += diff * accX / rate;
player->vel[2] += diff * accZ / rate;
player->pos[0] += (time - diff/rate) * accX / rate + diff * velX / rate;
player->pos[1] += -0.5 * grav * time * time + velY * time;
player->pos[2] += (time - diff/rate) * accZ / rate + diff * velZ / rate;
double scale = (double)MAP_BOX_SCALE;
double bound = scale - player->radius;
double posX = SDL_max(SDL_min(bound, player->pos[0]), -bound);
double posY = SDL_max(SDL_min(bound, player->pos[1]), player->height - scale);
double posZ = SDL_max(SDL_min(bound, player->pos[2]), -bound);
if (player->pos[0] != posX) player->vel[0] = 0;
if (player->pos[1] != posY) player->vel[1] = (wasd & 16) ? 8.4375 : 0;
if (player->pos[2] != posZ) player->vel[2] = 0;
player->pos[0] = posX;
player->pos[1] = posY;
player->pos[2] = posZ;
}
}
static void drawCircle(SDL_Renderer *renderer, float r, float x, float y)
{
float ang;
SDL_FPoint points[CIRCLE_DRAW_SIDES_LEN];
int i;
for (i = 0; i < CIRCLE_DRAW_SIDES_LEN; i++) {
ang = 2.0f * SDL_PI_F * (float)i / (float)CIRCLE_DRAW_SIDES;
points[i].x = x + r * SDL_cosf(ang);
points[i].y = y + r * SDL_sinf(ang);
}
SDL_RenderLines(renderer, (const SDL_FPoint*)&points, CIRCLE_DRAW_SIDES_LEN);
}
static void drawClippedSegment(
SDL_Renderer *renderer,
float ax, float ay, float az,
float bx, float by, float bz,
float x, float y, float z, float w)
{
if (az >= -w && bz >= -w) return;
float dx = ax - bx;
float dy = ay - by;
if (az > -w) {
float t = (-w - bz) / (az - bz);
ax = bx + dx * t;
ay = by + dy * t;
az = -w;
} else if (bz > -w) {
float t = (-w - az) / (bz - az);
bx = ax - dx * t;
by = ay - dy * t;
bz = -w;
}
ax = -z * ax / az;
ay = -z * ay / az;
bx = -z * bx / bz;
by = -z * by / bz;
SDL_RenderLine(renderer, x + ax, y - ay, x + bx, y - by);
}
static char debug_string[32];
static void draw(SDL_Renderer *renderer, const float (*edges)[6], const Player players[], int players_len)
{
int w, h, i, j, k;
if (!SDL_GetRenderOutputSize(renderer, &w, &h)) {
return;
}
SDL_SetRenderDrawColor(renderer, 0, 0, 0, SDL_ALPHA_OPAQUE);
SDL_RenderClear(renderer);
if (players_len > 0) {
float wf = (float)w;
float hf = (float)h;
int part_hor = players_len > 2 ? 2 : 1;
int part_ver = players_len > 1 ? 2 : 1;
float size_hor = wf / ((float)part_hor);
float size_ver = hf / ((float)part_ver);
for (i = 0; i < players_len; i++) {
const Player *player = &players[i];
float mod_x = (float)(i % part_hor);
float mod_y = (float)(i / part_hor);
float hor_origin = (mod_x + 0.5f) * size_hor;
float ver_origin = (mod_y + 0.5f) * size_ver;
float cam_origin = (float)(0.5 * SDL_sqrt(size_hor * size_hor + size_ver * size_ver));
float hor_offset = mod_x * size_hor;
float ver_offset = mod_y * size_ver;
SDL_Rect rect;
rect.x = (int)hor_offset;
rect.y = (int)ver_offset;
rect.w = (int)size_hor;
rect.h = (int)size_ver;
SDL_SetRenderClipRect(renderer, &rect);
double x0 = player->pos[0];
double y0 = player->pos[1];
double z0 = player->pos[2];
double bin_rad = SDL_PI_D / 2147483648.0;
double yaw_rad = bin_rad * player->yaw;
double pitch_rad = bin_rad * player->pitch;
double cos_yaw = SDL_cos( yaw_rad);
double sin_yaw = SDL_sin( yaw_rad);
double cos_pitch = SDL_cos(pitch_rad);
double sin_pitch = SDL_sin(pitch_rad);
double mat[9] = {
cos_yaw , 0, -sin_yaw ,
sin_yaw*sin_pitch, cos_pitch, cos_yaw*sin_pitch,
sin_yaw*cos_pitch, -sin_pitch, cos_yaw*cos_pitch
};
SDL_SetRenderDrawColor(renderer, 64, 64, 64, 255);
for (k = 0; k < MAP_BOX_EDGES_LEN; k++) {
const float *line = edges[k];
float ax = (float)(mat[0] * (line[0] - x0) + mat[1] * (line[1] - y0) + mat[2] * (line[2] - z0));
float ay = (float)(mat[3] * (line[0] - x0) + mat[4] * (line[1] - y0) + mat[5] * (line[2] - z0));
float az = (float)(mat[6] * (line[0] - x0) + mat[7] * (line[1] - y0) + mat[8] * (line[2] - z0));
float bx = (float)(mat[0] * (line[3] - x0) + mat[1] * (line[4] - y0) + mat[2] * (line[5] - z0));
float by = (float)(mat[3] * (line[3] - x0) + mat[4] * (line[4] - y0) + mat[5] * (line[5] - z0));
float bz = (float)(mat[6] * (line[3] - x0) + mat[7] * (line[4] - y0) + mat[8] * (line[5] - z0));
drawClippedSegment(renderer, ax, ay, az, bx, by, bz, hor_origin, ver_origin, cam_origin, 1);
}
for (j = 0; j < players_len; j++) {
if (i == j) continue;
const Player *target = &players[j];
SDL_SetRenderDrawColor(renderer, target->color[0], target->color[1], target->color[2], 255);
for (k = 0; k < 2; k++) {
double rx = target->pos[0] - player->pos[0];
double ry = target->pos[1] - player->pos[1] + (target->radius - target->height) * (float)k;
double rz = target->pos[2] - player->pos[2];
double dx = mat[0] * rx + mat[1] * ry + mat[2] * rz;
double dy = mat[3] * rx + mat[4] * ry + mat[5] * rz;
double dz = mat[6] * rx + mat[7] * ry + mat[8] * rz;
double r_eff = target->radius * cam_origin / dz;
if (!(dz < 0)) continue;
drawCircle(renderer, (float)(r_eff), (float)(hor_origin - cam_origin*dx/dz), (float)(ver_origin + cam_origin*dy/dz));
}
}
SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
SDL_RenderLine(renderer, hor_origin, ver_origin-10, hor_origin, ver_origin+10);
SDL_RenderLine(renderer, hor_origin-10, ver_origin, hor_origin+10, ver_origin);
}
}
SDL_SetRenderClipRect(renderer, 0);
SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
SDL_RenderDebugText(renderer, 0, 0, debug_string);
SDL_RenderPresent(renderer);
}
static void initPlayers(Player *players, int len)
{
int i;
for (i = 0; i < len; i++) {
players[i].pos[0] = 8.0 * (i & 1 ? -1.0 : 1.0);
players[i].pos[1] = 0;
players[i].pos[2] = 8.0 * (i & 1 ? -1.0 : 1.0) * (i & 2 ? -1.0 : 1.0);
players[i].vel[0] = 0;
players[i].vel[1] = 0;
players[i].vel[2] = 0;
players[i].yaw = 0x20000000 + (i & 1 ? 0x80000000 : 0) + (i & 2 ? 0x40000000 : 0);
players[i].pitch = -0x08000000;
players[i].radius = 0.5f;
players[i].height = 1.5f;
players[i].wasd = 0;
players[i].mouse = 0;
players[i].keyboard = 0;
players[i].color[0] = (1 << (i / 2)) & 2 ? 0 : 0xff;
players[i].color[1] = (1 << (i / 2)) & 1 ? 0 : 0xff;
players[i].color[2] = (1 << (i / 2)) & 4 ? 0 : 0xff;
players[i].color[0] = (i & 1) ? players[i].color[0] : ~players[i].color[0];
players[i].color[1] = (i & 1) ? players[i].color[1] : ~players[i].color[1];
players[i].color[2] = (i & 1) ? players[i].color[2] : ~players[i].color[2];
}
}
static void initEdges(int scale, float (*edges)[6], int edges_len)
{
int i, j;
const float r = (float)scale;
const int map[24] = {
0,1 , 1,3 , 3,2 , 2,0 ,
7,6 , 6,4 , 4,5 , 5,7 ,
6,2 , 3,7 , 0,4 , 5,1
};
for(i = 0; i < 12; i++) {
for (j = 0; j < 3; j++) {
edges[i][j+0] = (map[i*2+0] & (1 << j) ? r : -r);
edges[i][j+3] = (map[i*2+1] & (1 << j) ? r : -r);
}
}
for(i = 0; i < scale; i++) {
float d = (float)(i * 2);
for (j = 0; j < 2; j++) {
edges[i+12][3*j+0] = j ? r : -r;
edges[i+12][3*j+1] = -r;
edges[i+12][3*j+2] = d-r;
edges[i+12+scale][3*j+0] = d-r;
edges[i+12+scale][3*j+1] = -r;
edges[i+12+scale][3*j+2] = j ? r : -r;
}
}
}
SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[])
{
if (!SDL_SetAppMetadata("Example splitscreen shooter game", "1.0", "com.example.woodeneye-008")) {
return SDL_APP_FAILURE;
}
int i;
for (i = 0; i < SDL_arraysize(extended_metadata); i++) {
if (!SDL_SetAppMetadataProperty(extended_metadata[i].key, extended_metadata[i].value)) {
return SDL_APP_FAILURE;
}
}
AppState *as = SDL_calloc(1, sizeof(AppState));
if (!as) {
return SDL_APP_FAILURE;
} else {
*appstate = as;
}
if (!SDL_Init(SDL_INIT_VIDEO)) {
return SDL_APP_FAILURE;
}
if (!SDL_CreateWindowAndRenderer("examples/demo/woodeneye-008", 640, 480, 0, &as->window, &as->renderer)) {
return SDL_APP_FAILURE;
}
as->player_count = 1;
initPlayers(as->players, MAX_PLAYER_COUNT);
initEdges(MAP_BOX_SCALE, as->edges, MAP_BOX_EDGES_LEN);
debug_string[0] = 0;
SDL_SetRenderVSync(as->renderer, false);
SDL_SetWindowRelativeMouseMode(as->window, true);
SDL_SetHintWithPriority(SDL_HINT_WINDOWS_RAW_KEYBOARD, "1", SDL_HINT_OVERRIDE);
return SDL_APP_CONTINUE;
}
SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event)
{
AppState *as = appstate;
Player *players = as->players;
int player_count = as->player_count;
int i;
switch (event->type) {
case SDL_EVENT_QUIT:
return SDL_APP_SUCCESS;
break;
case SDL_EVENT_MOUSE_REMOVED:
for (i = 0; i < player_count; i++) {
if (players[i].mouse == event->mdevice.which) {
players[i].mouse = 0;
}
}
break;
case SDL_EVENT_KEYBOARD_REMOVED:
for (i = 0; i < player_count; i++) {
if (players[i].keyboard == event->kdevice.which) {
players[i].keyboard = 0;
}
}
break;
case SDL_EVENT_MOUSE_MOTION: {
SDL_MouseID id = event->motion.which;
int index = whoseMouse(id, players, player_count);
if (index >= 0) {
players[index].yaw -= ((int)event->motion.xrel) * 0x00080000;
players[index].pitch = SDL_max(-0x40000000, SDL_min(0x40000000, players[index].pitch - ((int)event->motion.yrel) * 0x00080000));
} else if (id) {
for (i = 0; i < MAX_PLAYER_COUNT; i++) {
if (players[i].mouse == 0) {
players[i].mouse = id;
as->player_count = SDL_max(as->player_count, i + 1);
break;
}
}
}
break;
}
case SDL_EVENT_MOUSE_BUTTON_DOWN: {
SDL_MouseID id = event->button.which;
int index = whoseMouse(id, players, player_count);
if (index >= 0) {
shoot(index, players, player_count);
}
break;
}
case SDL_EVENT_KEY_DOWN: {
SDL_Keycode sym = event->key.key;
SDL_KeyboardID id = event->key.which;
int index = whoseKeyboard(id, players, player_count);
if (index >= 0) {
if (sym == SDLK_W) players[index].wasd |= 1;
if (sym == SDLK_A) players[index].wasd |= 2;
if (sym == SDLK_S) players[index].wasd |= 4;
if (sym == SDLK_D) players[index].wasd |= 8;
if (sym == SDLK_SPACE) players[index].wasd |= 16;
} else if (id) {
for (i = 0; i < MAX_PLAYER_COUNT; i++) {
if (players[i].keyboard == 0) {
players[i].keyboard = id;
as->player_count = SDL_max(as->player_count, i + 1);
break;
}
}
}
break;
}
case SDL_EVENT_KEY_UP: {
SDL_Keycode sym = event->key.key;
SDL_KeyboardID id = event->key.which;
if (sym == SDLK_ESCAPE) return SDL_APP_SUCCESS;
int index = whoseKeyboard(id, players, player_count);
if (index >= 0) {
if (sym == SDLK_W) players[index].wasd &= 30;
if (sym == SDLK_A) players[index].wasd &= 29;
if (sym == SDLK_S) players[index].wasd &= 27;
if (sym == SDLK_D) players[index].wasd &= 23;
if (sym == SDLK_SPACE) players[index].wasd &= 15;
}
break;
}
}
return SDL_APP_CONTINUE;
}
SDL_AppResult SDL_AppIterate(void *appstate)
{
AppState *as = appstate;
static Uint64 accu = 0;
static Uint64 last = 0;
static Uint64 past = 0;
Uint64 now = SDL_GetTicksNS();
Uint64 dt_ns = now - past;
update(as->players, as->player_count, dt_ns);
draw(as->renderer, (const float (*)[6])as->edges, as->players, as->player_count);
if (now - last > 999999999) {
last = now;
SDL_snprintf(debug_string, sizeof(debug_string), "%" SDL_PRIu64 " fps", accu);
accu = 0;
}
past = now;
accu += 1;
Uint64 elapsed = SDL_GetTicksNS() - now;
if (elapsed < 999999) {
SDL_DelayNS(999999 - elapsed);
}
return SDL_APP_CONTINUE;
}
void SDL_AppQuit(void *appstate, SDL_AppResult result)
{
SDL_free(appstate); // just free the memory, SDL will clean up the window/renderer for us.
}

View file

@ -0,0 +1,7 @@
How many monkeys does it take to write the complete works of Shakespeare?
Now you can find out!
Cheer on your favorite monkey as they bash keyboards on their way through classic literature.

View file

@ -0,0 +1,377 @@
/*
* 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 char *text;
static const char *end;
static const char *progress;
static SDL_Time start_time;
static SDL_Time end_time;
typedef struct {
Uint32 *text;
int length;
} Line;
int row = 0;
int rows = 0;
int cols = 0;
static Line **lines;
static Line monkey_chars;
static int monkeys = 100;
/* The highest and lowest scancodes a monkey can hit */
#define MIN_MONKEY_SCANCODE SDL_SCANCODE_A
#define MAX_MONKEY_SCANCODE SDL_SCANCODE_SLASH
static const char *default_text =
"Jabberwocky, by Lewis Carroll\n"
"\n"
"'Twas brillig, and the slithy toves\n"
" Did gyre and gimble in the wabe:\n"
"All mimsy were the borogoves,\n"
" And the mome raths outgrabe.\n"
"\n"
"\"Beware the Jabberwock, my son!\n"
" The jaws that bite, the claws that catch!\n"
"Beware the Jubjub bird, and shun\n"
" The frumious Bandersnatch!\"\n"
"\n"
"He took his vorpal sword in hand;\n"
" Long time the manxome foe he sought-\n"
"So rested he by the Tumtum tree\n"
" And stood awhile in thought.\n"
"\n"
"And, as in uffish thought he stood,\n"
" The Jabberwock, with eyes of flame,\n"
"Came whiffling through the tulgey wood,\n"
" And burbled as it came!\n"
"\n"
"One, two! One, two! And through and through\n"
" The vorpal blade went snicker-snack!\n"
"He left it dead, and with its head\n"
" He went galumphing back.\n"
"\n"
"\"And hast thou slain the Jabberwock?\n"
" Come to my arms, my beamish boy!\n"
"O frabjous day! Callooh! Callay!\"\n"
" He chortled in his joy.\n"
"\n"
"'Twas brillig, and the slithy toves\n"
" Did gyre and gimble in the wabe:\n"
"All mimsy were the borogoves,\n"
" And the mome raths outgrabe.\n";
static void FreeLines(void)
{
int i;
if (rows > 0 && cols > 0) {
for (i = 0; i < rows; ++i) {
SDL_free(lines[i]->text);
SDL_free(lines[i]);
}
SDL_free(lines);
lines = NULL;
}
SDL_free(monkey_chars.text);
monkey_chars.text = NULL;
}
static void OnWindowSizeChanged(void)
{
int w, h;
if (!SDL_GetCurrentRenderOutputSize(renderer, &w, &h)) {
return;
}
FreeLines();
row = 0;
rows = (h / SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE) - 4;
cols = (w / SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE);
if (rows > 0 && cols > 0) {
int i;
lines = (Line **)SDL_malloc(rows * sizeof(Line *));
if (lines) {
for (i = 0; i < rows; ++i) {
lines[i] = (Line *)SDL_malloc(sizeof(Line));
if (!lines[i]) {
FreeLines();
break;
}
lines[i]->text = (Uint32 *)SDL_malloc(cols * sizeof(Uint32));
if (!lines[i]->text) {
FreeLines();
break;
}
lines[i]->length = 0;
}
}
monkey_chars.text = (Uint32 *)SDL_malloc(cols * sizeof(Uint32));
if (monkey_chars.text) {
for (i = 0; i < cols; ++i) {
monkey_chars.text[i] = ' ';
}
monkey_chars.length = cols;
}
}
}
/* This function runs once at startup. */
SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[])
{
int arg = 1;
SDL_SetAppMetadata("Infinite Monkeys", "1.0", "com.example.infinite-monkeys");
if (!SDL_Init(SDL_INIT_VIDEO)) {
SDL_Log("Couldn't initialize SDL: %s", SDL_GetError());
return SDL_APP_FAILURE;
}
if (!SDL_CreateWindowAndRenderer("examples/demo/infinite-monkeys", 640, 480, 0, &window, &renderer)) {
SDL_Log("Couldn't create window/renderer: %s", SDL_GetError());
return SDL_APP_FAILURE;
}
SDL_SetRenderVSync(renderer, 1);
if (argv[arg] && SDL_strcmp(argv[arg], "--monkeys") == 0) {
++arg;
if (argv[arg]) {
monkeys = SDL_atoi(argv[arg]);
++arg;
} else {
SDL_Log("Usage: %s [--monkeys N] [file.txt]", argv[0]);
return SDL_APP_FAILURE;
}
}
if (argv[arg]) {
const char *file = argv[arg];
size_t size;
text = (char *)SDL_LoadFile(file, &size);
if (!text) {
SDL_Log("Couldn't open %s: %s", file, SDL_GetError());
return SDL_APP_FAILURE;
}
end = text + size;
} else {
text = SDL_strdup(default_text);
end = text + SDL_strlen(text);
}
progress = text;
SDL_GetCurrentTime(&start_time);
OnWindowSizeChanged();
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)
{
switch (event->type) {
case SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED:
OnWindowSizeChanged();
break;
case SDL_EVENT_QUIT:
return SDL_APP_SUCCESS; /* end the program, reporting success to the OS. */
}
return SDL_APP_CONTINUE; /* carry on with the program! */
}
static void DisplayLine(float x, float y, Line *line)
{
/* Allocate maximum space potentially needed for this line */
char *utf8 = (char *)SDL_malloc(line->length * 4 + 1);
if (utf8) {
char *spot = utf8;
int i;
for (i = 0; i < line->length; ++i) {
spot = SDL_UCS4ToUTF8(line->text[i], spot);
}
*spot = '\0';
SDL_RenderDebugText(renderer, x, y, utf8);
SDL_free(utf8);
}
}
static bool CanMonkeyType(Uint32 ch)
{
SDL_Keymod modstate;
SDL_Scancode scancode = SDL_GetScancodeFromKey(ch, &modstate);
if (scancode < MIN_MONKEY_SCANCODE || scancode > MAX_MONKEY_SCANCODE) {
return false;
}
/* Monkeys can hit the shift key, but nothing else */
if ((modstate & ~SDL_KMOD_SHIFT) != 0) {
return false;
}
return true;
}
static void AdvanceRow(void)
{
Line *line;
++row;
line = lines[row % rows];
line->length = 0;
}
static void AddMonkeyChar(int monkey, Uint32 ch)
{
if (monkey >= 0 && monkey_chars.text) {
monkey_chars.text[(monkey % cols)] = ch;
}
if (lines) {
if (ch == '\n') {
AdvanceRow();
} else {
Line *line = lines[row % rows];
line->text[line->length++] = ch;
if (line->length == cols) {
AdvanceRow();
}
}
}
SDL_StepUTF8(&progress, NULL);
}
static Uint32 GetNextChar(void)
{
Uint32 ch = 0;
while (progress < end) {
const char *spot = progress;
ch = SDL_StepUTF8(&spot, NULL);
if (CanMonkeyType(ch)) {
break;
} else {
/* This is a freebie, monkeys can't type this */
AddMonkeyChar(-1, ch);
}
}
return ch;
}
static Uint32 MonkeyPlay(void)
{
int count = (MAX_MONKEY_SCANCODE - MIN_MONKEY_SCANCODE + 1);
SDL_Scancode scancode = (SDL_Scancode)(MIN_MONKEY_SCANCODE + SDL_rand(count));
SDL_Keymod modstate = (SDL_rand(2) ? SDL_KMOD_SHIFT : 0);
return SDL_GetKeyFromScancode(scancode, modstate, false);
}
/* This function runs once per frame, and is the heart of the program. */
SDL_AppResult SDL_AppIterate(void *appstate)
{
int i, monkey;
Uint32 next_char = 0, ch;
float x, y;
char *caption = NULL;
SDL_Time now, elapsed;
int hours, minutes, seconds;
SDL_FRect rect;
for (monkey = 0; monkey < monkeys; ++monkey) {
if (next_char == 0) {
next_char = GetNextChar();
if (!next_char) {
/* All done! */
break;
}
}
ch = MonkeyPlay();
if (ch == next_char) {
AddMonkeyChar(monkey, ch);
next_char = 0;
}
}
/* Clear the screen */
SDL_SetRenderDrawColor(renderer, 0, 0, 0, SDL_ALPHA_OPAQUE);
SDL_RenderClear(renderer);
/* Show the text already decoded */
SDL_SetRenderDrawColor(renderer, 255, 255, 255, SDL_ALPHA_OPAQUE);
x = 0.0f;
y = 0.0f;
if (lines) {
int row_offset = row - rows + 1;
if (row_offset < 0) {
row_offset = 0;
}
for (i = 0; i < rows; ++i) {
Line *line = lines[(row_offset + i) % rows];
DisplayLine(x, y, line);
y += SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE;
}
/* Show the caption */
y = (float)((rows + 1) * SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE);
if (progress == end) {
if (!end_time) {
SDL_GetCurrentTime(&end_time);
}
now = end_time;
} else {
SDL_GetCurrentTime(&now);
}
elapsed = (now - start_time);
elapsed /= SDL_NS_PER_SECOND;
seconds = (int)(elapsed % 60);
elapsed /= 60;
minutes = (int)(elapsed % 60);
elapsed /= 60;
hours = (int)elapsed;
SDL_asprintf(&caption, "Monkeys: %d - %dH:%dM:%dS", monkeys, hours, minutes, seconds);
if (caption) {
SDL_RenderDebugText(renderer, x, y, caption);
SDL_free(caption);
}
y += SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE;
/* Show the characters currently typed */
DisplayLine(x, y, &monkey_chars);
y += SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE;
}
/* Show the current progress */
SDL_SetRenderDrawColor(renderer, 0, 255, 0, SDL_ALPHA_OPAQUE);
rect.x = x;
rect.y = y;
rect.w = ((float)(progress - text) / (end - text)) * (cols * SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE);
rect.h = (float)SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE;
SDL_RenderFillRect(renderer, &rect);
SDL_RenderPresent(renderer);
return SDL_APP_CONTINUE; /* carry on with the program! */
}
/* This function runs once at shutdown. */
void SDL_AppQuit(void *appstate, SDL_AppResult result)
{
/* SDL will clean up the window/renderer for us. */
FreeLines();
SDL_free(text);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 93 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

View file

@ -0,0 +1,4 @@
An implementation of the BytePusher VM
For example programs and more information about BytePusher, see
https://esolangs.org/wiki/BytePusher

View file

@ -0,0 +1,416 @@
/*
* An implementation of the BytePusher VM.
*
* For example programs and more information about BytePusher, see
* https://esolangs.org/wiki/BytePusher
*
* This code is public domain. Feel free to use it for any purpose!
*/
#define SDL_MAIN_USE_CALLBACKS
#include <SDL3/SDL.h>
#include <SDL3/SDL_main.h>
#include <stdarg.h>
#define SCREEN_W 256
#define SCREEN_H 256
#define RAM_SIZE 0x1000000
#define FRAMES_PER_SECOND 60
#define SAMPLES_PER_FRAME 256
#define NS_PER_SECOND (Uint64)SDL_NS_PER_SECOND
#define MAX_AUDIO_LATENCY_FRAMES 5
#define IO_KEYBOARD 0
#define IO_PC 2
#define IO_SCREEN_PAGE 5
#define IO_AUDIO_BANK 6
typedef struct {
Uint8 ram[RAM_SIZE + 8];
Uint8 screenbuf[SCREEN_W * SCREEN_H];
Uint64 last_tick;
Uint64 tick_acc;
SDL_Window* window;
SDL_Renderer* renderer;
SDL_Surface* screen;
SDL_Texture* screentex;
SDL_Texture* rendertarget; /* we need this render target for text to look good */
SDL_AudioStream* audiostream;
char status[SCREEN_W / 8];
int status_ticks;
Uint16 keystate;
bool display_help;
bool positional_input;
} BytePusher;
static const struct {
const char *key;
const char *value;
} extended_metadata[] = {
{ SDL_PROP_APP_METADATA_URL_STRING, "https://examples.libsdl.org/SDL3/demo/04-bytepusher/" },
{ SDL_PROP_APP_METADATA_CREATOR_STRING, "SDL team" },
{ SDL_PROP_APP_METADATA_COPYRIGHT_STRING, "Placed in the public domain" },
{ SDL_PROP_APP_METADATA_TYPE_STRING, "game" }
};
static inline Uint16 read_u16(const BytePusher* vm, Uint32 addr) {
const Uint8* ptr = &vm->ram[addr];
return ((Uint16)ptr[0] << 8) | ((Uint16)ptr[1]);
}
static inline Uint32 read_u24(const BytePusher* vm, Uint32 addr) {
const Uint8* ptr = &vm->ram[addr];
return ((Uint32)ptr[0] << 16) | ((Uint32)ptr[1] << 8) | ((Uint32)ptr[2]);
}
static void set_status(BytePusher* vm, const char* fmt, ...) {
va_list args;
va_start(args, fmt);
SDL_vsnprintf(vm->status, sizeof(vm->status), fmt, args);
va_end(args);
vm->status[sizeof(vm->status) - 1] = 0;
vm->status_ticks = FRAMES_PER_SECOND * 3;
}
static bool load(BytePusher* vm, SDL_IOStream* stream, bool closeio) {
size_t bytes_read = 0;
bool ok = true;
SDL_memset(vm->ram, 0, RAM_SIZE);
if (!stream) {
return false;
}
while (bytes_read < RAM_SIZE) {
size_t read = SDL_ReadIO(stream, &vm->ram[bytes_read], RAM_SIZE - bytes_read);
bytes_read += read;
if (read == 0) {
ok = SDL_GetIOStatus(stream) == SDL_IO_STATUS_EOF;
break;
}
}
if (closeio) {
SDL_CloseIO(stream);
}
SDL_ClearAudioStream(vm->audiostream);
vm->display_help = !ok;
return ok;
}
static const char* filename(const char* path) {
size_t i = SDL_strlen(path) + 1;
while (i > 0) {
i -= 1;
if (path[i] == '/' || path[i] == '\\') {
return path + i + 1;
}
}
return path;
}
static bool load_file(BytePusher* vm, const char* path) {
if (load(vm, SDL_IOFromFile(path, "rb"), true)) {
set_status(vm, "loaded %s", filename(path));
return true;
} else {
set_status(vm, "load failed: %s", filename(path));
return false;
}
}
static void print(BytePusher* vm, int x, int y, const char* str) {
SDL_SetRenderDrawColor(vm->renderer, 0, 0, 0, SDL_ALPHA_OPAQUE);
SDL_RenderDebugText(vm->renderer, (float)(x + 1), (float)(y + 1), str);
SDL_SetRenderDrawColor(vm->renderer, 0xff, 0xff, 0xff, SDL_ALPHA_OPAQUE);
SDL_RenderDebugText(vm->renderer, (float)x, (float)y, str);
SDL_SetRenderDrawColor(vm->renderer, 0, 0, 0, SDL_ALPHA_OPAQUE);
}
SDL_AppResult SDL_AppInit(void** appstate, int argc, char* argv[]) {
BytePusher* vm;
SDL_Palette* palette;
SDL_Rect usable_bounds;
SDL_AudioSpec audiospec = { SDL_AUDIO_S8, 1, SAMPLES_PER_FRAME * FRAMES_PER_SECOND };
SDL_DisplayID primary_display;
SDL_PropertiesID texprops;
int zoom = 2;
int i;
Uint8 r, g, b;
(void)argc;
(void)argv;
if (!SDL_SetAppMetadata("SDL 3 BytePusher", "1.0", "com.example.SDL3BytePusher")) {
return SDL_APP_FAILURE;
}
for (i = 0; i < (int)SDL_arraysize(extended_metadata); i++) {
if (!SDL_SetAppMetadataProperty(extended_metadata[i].key, extended_metadata[i].value)) {
return SDL_APP_FAILURE;
}
}
if (!SDL_Init(SDL_INIT_AUDIO | SDL_INIT_VIDEO)) {
return SDL_APP_FAILURE;
}
if (!(vm = (BytePusher *)SDL_calloc(1, sizeof(*vm)))) {
return SDL_APP_FAILURE;
}
*(BytePusher**)appstate = vm;
vm->display_help = true;
primary_display = SDL_GetPrimaryDisplay();
if (SDL_GetDisplayUsableBounds(primary_display, &usable_bounds)) {
int zoom_w = (usable_bounds.w - usable_bounds.x) * 2 / 3 / SCREEN_W;
int zoom_h = (usable_bounds.h - usable_bounds.y) * 2 / 3 / SCREEN_H;
zoom = zoom_w < zoom_h ? zoom_w : zoom_h;
if (zoom < 1) {
zoom = 1;
}
}
if (!SDL_CreateWindowAndRenderer("SDL 3 BytePusher",
SCREEN_W * zoom, SCREEN_H * zoom, SDL_WINDOW_RESIZABLE,
&vm->window, &vm->renderer
)) {
return SDL_APP_FAILURE;
}
if (!SDL_SetRenderLogicalPresentation(
vm->renderer, SCREEN_W, SCREEN_H, SDL_LOGICAL_PRESENTATION_INTEGER_SCALE
)) {
return SDL_APP_FAILURE;
}
if (!(vm->screen = SDL_CreateSurfaceFrom(
SCREEN_W, SCREEN_H, SDL_PIXELFORMAT_INDEX8, vm->screenbuf, SCREEN_W
))) {
return SDL_APP_FAILURE;
}
if (!(palette = SDL_CreateSurfacePalette(vm->screen))) {
return SDL_APP_FAILURE;
}
i = 0;
for (r = 0; r < 6; ++r) {
for (g = 0; g < 6; ++g) {
for (b = 0; b < 6; ++b, ++i) {
SDL_Color color = { (Uint8)(r * 0x33), (Uint8)(g * 0x33), (Uint8)(b * 0x33), SDL_ALPHA_OPAQUE };
palette->colors[i] = color;
}
}
}
for (; i < 256; ++i) {
SDL_Color color = { 0, 0, 0, SDL_ALPHA_OPAQUE };
palette->colors[i] = color;
}
texprops = SDL_CreateProperties();
SDL_SetNumberProperty(texprops, SDL_PROP_TEXTURE_CREATE_ACCESS_NUMBER, SDL_TEXTUREACCESS_STREAMING);
SDL_SetNumberProperty(texprops, SDL_PROP_TEXTURE_CREATE_WIDTH_NUMBER, SCREEN_W);
SDL_SetNumberProperty(texprops, SDL_PROP_TEXTURE_CREATE_HEIGHT_NUMBER, SCREEN_H);
vm->screentex = SDL_CreateTextureWithProperties(vm->renderer, texprops);
SDL_SetNumberProperty(texprops, SDL_PROP_TEXTURE_CREATE_ACCESS_NUMBER, SDL_TEXTUREACCESS_TARGET);
vm->rendertarget = SDL_CreateTextureWithProperties(vm->renderer, texprops);
SDL_DestroyProperties(texprops);
if (!vm->screentex || !vm->rendertarget) {
return SDL_APP_FAILURE;
}
SDL_SetTextureScaleMode(vm->screentex, SDL_SCALEMODE_NEAREST);
SDL_SetTextureScaleMode(vm->rendertarget, SDL_SCALEMODE_NEAREST);
if (!(vm->audiostream = SDL_OpenAudioDeviceStream(
SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &audiospec, NULL, NULL
))) {
return SDL_APP_FAILURE;
}
SDL_SetAudioStreamGain(vm->audiostream, 0.1f); /* examples are loud! */
SDL_ResumeAudioStreamDevice(vm->audiostream);
set_status(vm, "renderer: %s", SDL_GetRendererName(vm->renderer));
vm->last_tick = SDL_GetTicksNS();
vm->tick_acc = NS_PER_SECOND;
return SDL_APP_CONTINUE;
}
SDL_AppResult SDL_AppIterate(void* appstate) {
BytePusher* vm = (BytePusher*)appstate;
Uint64 tick = SDL_GetTicksNS();
Uint64 delta = tick - vm->last_tick;
bool updated, skip_audio;
vm->last_tick = tick;
vm->tick_acc += delta * FRAMES_PER_SECOND;
updated = vm->tick_acc >= NS_PER_SECOND;
skip_audio = vm->tick_acc >= MAX_AUDIO_LATENCY_FRAMES * NS_PER_SECOND;
if (skip_audio) {
// don't let audio fall too far behind
SDL_ClearAudioStream(vm->audiostream);
}
while (vm->tick_acc >= NS_PER_SECOND) {
Uint32 pc;
int i;
vm->tick_acc -= NS_PER_SECOND;
vm->ram[IO_KEYBOARD] = (Uint8)(vm->keystate >> 8);
vm->ram[IO_KEYBOARD + 1] = (Uint8)(vm->keystate);
pc = read_u24(vm, IO_PC);
for (i = 0; i < SCREEN_W * SCREEN_H; ++i) {
Uint32 src = read_u24(vm, pc);
Uint32 dst = read_u24(vm, pc + 3);
vm->ram[dst] = vm->ram[src];
pc = read_u24(vm, pc + 6);
}
if (!skip_audio || vm->tick_acc < NS_PER_SECOND) {
SDL_PutAudioStreamData(
vm->audiostream,
&vm->ram[(Uint32)read_u16(vm, IO_AUDIO_BANK) << 8],
SAMPLES_PER_FRAME
);
}
}
if (updated) {
SDL_Surface *tex;
SDL_SetRenderTarget(vm->renderer, vm->rendertarget);
if (!SDL_LockTextureToSurface(vm->screentex, NULL, &tex)) {
return SDL_APP_FAILURE;
}
vm->screen->pixels = &vm->ram[(Uint32)vm->ram[IO_SCREEN_PAGE] << 16];
SDL_BlitSurface(vm->screen, NULL, tex, NULL);
SDL_UnlockTexture(vm->screentex);
SDL_RenderTexture(vm->renderer, vm->screentex, NULL, NULL);
}
if (vm->display_help) {
print(vm, 4, 4, "Drop a BytePusher file in this");
print(vm, 8, 12, "window to load and run it!");
print(vm, 4, 28, "Press ENTER to switch between");
print(vm, 8, 36, "positional and symbolic input.");
}
if (vm->status_ticks > 0) {
vm->status_ticks -= 1;
print(vm, 4, SCREEN_H - 12, vm->status);
}
SDL_SetRenderTarget(vm->renderer, NULL);
SDL_RenderClear(vm->renderer);
SDL_RenderTexture(vm->renderer, vm->rendertarget, NULL, NULL);
SDL_RenderPresent(vm->renderer);
return SDL_APP_CONTINUE;
}
static Uint16 keycode_mask(SDL_Keycode key) {
int index;
if (key >= SDLK_0 && key <= SDLK_9) {
index = key - SDLK_0;
} else if (key >= SDLK_A && key <= SDLK_F) {
index = key - SDLK_A + 10;
} else {
return 0;
}
return (Uint16)1 << index;
}
static Uint16 scancode_mask(SDL_Scancode scancode) {
int index;
switch (scancode) {
case SDL_SCANCODE_1: index = 0x1; break;
case SDL_SCANCODE_2: index = 0x2; break;
case SDL_SCANCODE_3: index = 0x3; break;
case SDL_SCANCODE_4: index = 0xc; break;
case SDL_SCANCODE_Q: index = 0x4; break;
case SDL_SCANCODE_W: index = 0x5; break;
case SDL_SCANCODE_E: index = 0x6; break;
case SDL_SCANCODE_R: index = 0xd; break;
case SDL_SCANCODE_A: index = 0x7; break;
case SDL_SCANCODE_S: index = 0x8; break;
case SDL_SCANCODE_D: index = 0x9; break;
case SDL_SCANCODE_F: index = 0xe; break;
case SDL_SCANCODE_Z: index = 0xa; break;
case SDL_SCANCODE_X: index = 0x0; break;
case SDL_SCANCODE_C: index = 0xb; break;
case SDL_SCANCODE_V: index = 0xf; break;
default: return 0;
}
return (Uint16)1 << index;
}
SDL_AppResult SDL_AppEvent(void* appstate, SDL_Event* event) {
BytePusher* vm = (BytePusher*)appstate;
switch (event->type) {
case SDL_EVENT_QUIT:
return SDL_APP_SUCCESS;
case SDL_EVENT_DROP_FILE:
load_file(vm, event->drop.data);
break;
case SDL_EVENT_KEY_DOWN:
#ifndef __EMSCRIPTEN__
if (event->key.key == SDLK_ESCAPE) {
return SDL_APP_SUCCESS;
}
#endif
if (event->key.key == SDLK_RETURN) {
vm->positional_input = !vm->positional_input;
vm->keystate = 0;
if (vm->positional_input) {
set_status(vm, "switched to positional input");
} else {
set_status(vm, "switched to symbolic input");
}
}
if (vm->positional_input) {
vm->keystate |= scancode_mask(event->key.scancode);
} else {
vm->keystate |= keycode_mask(event->key.key);
}
break;
case SDL_EVENT_KEY_UP:
if (vm->positional_input) {
vm->keystate &= ~scancode_mask(event->key.scancode);
} else {
vm->keystate &= ~keycode_mask(event->key.key);
}
break;
}
return SDL_APP_CONTINUE;
}
void SDL_AppQuit(void* appstate, SDL_AppResult result) {
if (result == SDL_APP_FAILURE) {
SDL_Log("Error: %s", SDL_GetError());
}
if (appstate) {
BytePusher* vm = (BytePusher*)appstate;
SDL_DestroyAudioStream(vm->audiostream);
SDL_DestroyTexture(vm->rendertarget);
SDL_DestroyTexture(vm->screentex);
SDL_DestroySurface(vm->screen);
SDL_DestroyRenderer(vm->renderer);
SDL_DestroyWindow(vm->window);
SDL_free(vm);
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 420 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

View file

@ -0,0 +1 @@
Full game and app demos

View file

@ -0,0 +1,79 @@
-- 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 ( (token == "Uint8") or (token == "Uint16") or (token == "Uint32") or (token == "Uint64") or
(token == "Sint8") or (token == "Sint16") or (token == "Sint32") or (token == "Sint64") or
(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 },
}

View file

@ -0,0 +1,2 @@
This example code looks for the current joystick state once per frame,
and draws a visual representation of it.

View file

@ -0,0 +1,193 @@
/*
* This example code looks for the current joystick state once per frame,
* and draws a visual representation of it.
*
* This code is public domain. Feel free to use it for any purpose!
*/
/* Joysticks are low-level interfaces: there's something with a bunch of
buttons, axes and hats, in no understood order or position. This is
a flexible interface, but you'll need to build some sort of configuration
UI to let people tell you what button, etc, does what. On top of this
interface, SDL offers the "gamepad" API, which works with lots of devices,
and knows how to map arbitrary buttons and such to look like an
Xbox/PlayStation/etc gamepad. This is easier, and better, for many games,
but isn't necessarily a good fit for complex apps and hardware. A flight
simulator, a realistic racing game, etc, might want this interface instead
of gamepads. */
/* SDL can handle multiple joysticks, but for simplicity, this program only
deals with the first stick it sees. */
#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_Joystick *joystick = NULL;
static SDL_Color colors[64];
/* This function runs once at startup. */
SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[])
{
int i;
SDL_SetAppMetadata("Example Input Joystick Polling", "1.0", "com.example.input-joystick-polling");
if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK)) {
SDL_Log("Couldn't initialize SDL: %s", SDL_GetError());
return SDL_APP_FAILURE;
}
if (!SDL_CreateWindowAndRenderer("examples/input/joystick-polling", 640, 480, 0, &window, &renderer)) {
SDL_Log("Couldn't create window/renderer: %s", SDL_GetError());
return SDL_APP_FAILURE;
}
for (i = 0; i < SDL_arraysize(colors); i++) {
colors[i].r = SDL_rand(255);
colors[i].g = SDL_rand(255);
colors[i].b = SDL_rand(255);
colors[i].a = 255;
}
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_JOYSTICK_ADDED) {
/* this event is sent for each hotplugged stick, but also each already-connected joystick during SDL_Init(). */
if (joystick == NULL) { /* we don't have a stick yet and one was added, open it! */
joystick = SDL_OpenJoystick(event->jdevice.which);
if (!joystick) {
SDL_Log("Failed to open joystick ID %u: %s", (unsigned int) event->jdevice.which, SDL_GetError());
}
}
} else if (event->type == SDL_EVENT_JOYSTICK_REMOVED) {
if (joystick && (SDL_GetJoystickID(joystick) == event->jdevice.which)) {
SDL_CloseJoystick(joystick); /* our joystick was unplugged. */
joystick = NULL;
}
}
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)
{
int winw = 640, winh = 480;
const char *text = "Plug in a joystick, please.";
float x, y;
int i;
if (joystick) { /* we have a stick opened? */
text = SDL_GetJoystickName(joystick);
}
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
SDL_RenderClear(renderer);
SDL_GetWindowSize(window, &winw, &winh);
/* note that you can get input as events, instead of polling, which is
better since it won't miss button presses if the system is lagging,
but often times checking the current state per-frame is good enough,
and maybe better if you'd rather _drop_ inputs due to lag. */
if (joystick) { /* we have a stick opened? */
const float size = 30.0f;
int total;
/* draw axes as bars going across middle of screen. We don't know if it's an X or Y or whatever axis, so we can't do more than this. */
total = SDL_GetNumJoystickAxes(joystick);
y = (winh - (total * size)) / 2;
x = ((float) winw) / 2.0f;
for (i = 0; i < total; i++) {
const SDL_Color *color = &colors[i % SDL_arraysize(colors)];
const float val = (((float) SDL_GetJoystickAxis(joystick, i)) / 32767.0f); /* make it -1.0f to 1.0f */
const float dx = x + (val * x);
const SDL_FRect dst = { dx, y, x - SDL_fabsf(dx), size };
SDL_SetRenderDrawColor(renderer, color->r, color->g, color->b, color->a);
SDL_RenderFillRect(renderer, &dst);
y += size;
}
/* draw buttons as blocks across top of window. We only know the button numbers, but not where they are on the device. */
total = SDL_GetNumJoystickButtons(joystick);
x = (winw - (total * size)) / 2;
for (i = 0; i < total; i++) {
const SDL_Color *color = &colors[i % SDL_arraysize(colors)];
const SDL_FRect dst = { x, 0.0f, size, size };
if (SDL_GetJoystickButton(joystick, i)) {
SDL_SetRenderDrawColor(renderer, color->r, color->g, color->b, color->a);
} else {
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
}
SDL_RenderFillRect(renderer, &dst);
SDL_SetRenderDrawColor(renderer, 255, 255, 255, color->a);
SDL_RenderRect(renderer, &dst); /* outline it */
x += size;
}
/* draw hats across the bottom of the screen. */
total = SDL_GetNumJoystickHats(joystick);
x = ((winw - (total * (size * 2.0f))) / 2.0f) + (size / 2.0f);
y = ((float) winh) - size;
for (i = 0; i < total; i++) {
const SDL_Color *color = &colors[i % SDL_arraysize(colors)];
const float thirdsize = size / 3.0f;
const SDL_FRect cross[] = { { x, y + thirdsize, size, thirdsize }, { x + thirdsize, y, thirdsize, size } };
const Uint8 hat = SDL_GetJoystickHat(joystick, i);
SDL_SetRenderDrawColor(renderer, 90, 90, 90, 255);
SDL_RenderFillRects(renderer, cross, SDL_arraysize(cross));
SDL_SetRenderDrawColor(renderer, color->r, color->g, color->b, color->a);
if (hat & SDL_HAT_UP) {
const SDL_FRect dst = { x + thirdsize, y, thirdsize, thirdsize };
SDL_RenderFillRect(renderer, &dst);
}
if (hat & SDL_HAT_RIGHT) {
const SDL_FRect dst = { x + (thirdsize * 2), y + thirdsize, thirdsize, thirdsize };
SDL_RenderFillRect(renderer, &dst);
}
if (hat & SDL_HAT_DOWN) {
const SDL_FRect dst = { x + thirdsize, y + (thirdsize * 2), thirdsize, thirdsize };
SDL_RenderFillRect(renderer, &dst);
}
if (hat & SDL_HAT_LEFT) {
const SDL_FRect dst = { x, y + thirdsize, thirdsize, thirdsize };
SDL_RenderFillRect(renderer, &dst);
}
x += size * 2;
}
}
x = (((float) winw) - (SDL_strlen(text) * SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE)) / 2.0f;
y = (((float) winh) - SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE) / 2.0f;
SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
SDL_RenderDebugText(renderer, x, y, text);
SDL_RenderPresent(renderer);
return SDL_APP_CONTINUE; /* carry on with the program! */
}
/* This function runs once at shutdown. */
void SDL_AppQuit(void *appstate, SDL_AppResult result)
{
if (joystick) {
SDL_CloseJoystick(joystick);
}
/* SDL will clean up the window/renderer for us. */
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

View file

@ -0,0 +1,2 @@
This example code looks for joystick input in the event handler, and
reports any changes as a flood of info.

View file

@ -0,0 +1,232 @@
/*
* This example code looks for joystick input in the event handler, and
* reports any changes as a flood of info.
*
* This code is public domain. Feel free to use it for any purpose!
*/
/* Joysticks are low-level interfaces: there's something with a bunch of
buttons, axes and hats, in no understood order or position. This is
a flexible interface, but you'll need to build some sort of configuration
UI to let people tell you what button, etc, does what. On top of this
interface, SDL offers the "gamepad" API, which works with lots of devices,
and knows how to map arbitrary buttons and such to look like an
Xbox/PlayStation/etc gamepad. This is easier, and better, for many games,
but isn't necessarily a good fit for complex apps and hardware. A flight
simulator, a realistic racing game, etc, might want this interface instead
of gamepads. */
#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_Color colors[64];
#define MOTION_EVENT_COOLDOWN 40
typedef struct EventMessage
{
char *str;
SDL_Color color;
Uint64 start_ticks;
struct EventMessage *next;
} EventMessage;
static EventMessage messages;
static EventMessage *messages_tail = &messages;
static const char *hat_state_string(Uint8 state)
{
switch (state) {
case SDL_HAT_CENTERED: return "CENTERED";
case SDL_HAT_UP: return "UP";
case SDL_HAT_RIGHT: return "RIGHT";
case SDL_HAT_DOWN: return "DOWN";
case SDL_HAT_LEFT: return "LEFT";
case SDL_HAT_RIGHTUP: return "RIGHT+UP";
case SDL_HAT_RIGHTDOWN: return "RIGHT+DOWN";
case SDL_HAT_LEFTUP: return "LEFT+UP";
case SDL_HAT_LEFTDOWN: return "LEFT+DOWN";
default: break;
}
return "UNKNOWN";
}
static const char *battery_state_string(SDL_PowerState state)
{
switch (state) {
case SDL_POWERSTATE_ERROR: return "ERROR";
case SDL_POWERSTATE_UNKNOWN: return "UNKNOWN";
case SDL_POWERSTATE_ON_BATTERY: return "ON BATTERY";
case SDL_POWERSTATE_NO_BATTERY: return "NO BATTERY";
case SDL_POWERSTATE_CHARGING: return "CHARGING";
case SDL_POWERSTATE_CHARGED: return "CHARGED";
default: break;
}
return "UNKNOWN";
}
static void add_message(SDL_JoystickID jid, const char *fmt, ...)
{
const SDL_Color *color = &colors[((size_t) jid) % SDL_arraysize(colors)];
EventMessage *msg = NULL;
char *str = NULL;
va_list ap;
msg = (EventMessage *) SDL_calloc(1, sizeof (*msg));
if (!msg) {
return; // oh well.
}
va_start(ap, fmt);
SDL_vasprintf(&str, fmt, ap);
va_end(ap);
if (!str) {
SDL_free(msg);
return; // oh well.
}
msg->str = str;
SDL_copyp(&msg->color, color);
msg->start_ticks = SDL_GetTicks();
msg->next = NULL;
messages_tail->next = msg;
messages_tail = msg;
}
/* This function runs once at startup. */
SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[])
{
int i;
SDL_SetAppMetadata("Example Input Joystick Events", "1.0", "com.example.input-joystick-events");
if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK)) {
SDL_Log("Couldn't initialize SDL: %s", SDL_GetError());
return SDL_APP_FAILURE;
}
if (!SDL_CreateWindowAndRenderer("examples/input/joystick-events", 640, 480, 0, &window, &renderer)) {
SDL_Log("Couldn't create window/renderer: %s", SDL_GetError());
return SDL_APP_FAILURE;
}
colors[0].r = colors[0].g = colors[0].b = colors[0].a = 255;
for (i = 1; i < SDL_arraysize(colors); i++) {
colors[i].r = SDL_rand(255);
colors[i].g = SDL_rand(255);
colors[i].b = SDL_rand(255);
colors[i].a = 255;
}
add_message(0, "Please plug in a joystick.");
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_JOYSTICK_ADDED) {
/* this event is sent for each hotplugged stick, but also each already-connected joystick during SDL_Init(). */
const SDL_JoystickID which = event->jdevice.which;
SDL_Joystick *joystick = SDL_OpenJoystick(which);
if (!joystick) {
add_message(which, "Joystick #%u add, but not opened: %s", (unsigned int) which, SDL_GetError());
} else {
add_message(which, "Joystick #%u ('%s') added", (unsigned int) which, SDL_GetJoystickName(joystick));
}
} else if (event->type == SDL_EVENT_JOYSTICK_REMOVED) {
const SDL_JoystickID which = event->jdevice.which;
SDL_Joystick *joystick = SDL_GetJoystickFromID(which);
if (joystick) {
SDL_CloseJoystick(joystick); /* the joystick was unplugged. */
}
add_message(which, "Joystick #%u removed", (unsigned int) which);
} else if (event->type == SDL_EVENT_JOYSTICK_AXIS_MOTION) {
static Uint64 axis_motion_cooldown_time = 0; /* these are spammy, only show every X milliseconds. */
const Uint64 now = SDL_GetTicks();
if (now >= axis_motion_cooldown_time) {
const SDL_JoystickID which = event->jaxis.which;
axis_motion_cooldown_time = now + MOTION_EVENT_COOLDOWN;
add_message(which, "Joystick #%u axis %d -> %d", (unsigned int) which, (int) event->jaxis.axis, (int) event->jaxis.value);
}
} else if (event->type == SDL_EVENT_JOYSTICK_BALL_MOTION) {
static Uint64 ball_motion_cooldown_time = 0; /* these are spammy, only show every X milliseconds. */
const Uint64 now = SDL_GetTicks();
if (now >= ball_motion_cooldown_time) {
const SDL_JoystickID which = event->jball.which;
ball_motion_cooldown_time = now + MOTION_EVENT_COOLDOWN;
add_message(which, "Joystick #%u ball %d -> %d, %d", (unsigned int) which, (int) event->jball.ball, (int) event->jball.xrel, (int) event->jball.yrel);
}
} else if (event->type == SDL_EVENT_JOYSTICK_HAT_MOTION) {
const SDL_JoystickID which = event->jhat.which;
add_message(which, "Joystick #%u hat %d -> %s", (unsigned int) which, (int) event->jhat.hat, hat_state_string(event->jhat.value));
} else if ((event->type == SDL_EVENT_JOYSTICK_BUTTON_UP) || (event->type == SDL_EVENT_JOYSTICK_BUTTON_DOWN)) {
const SDL_JoystickID which = event->jbutton.which;
add_message(which, "Joystick #%u button %d -> %s", (unsigned int) which, (int) event->jbutton.button, event->jbutton.down ? "PRESSED" : "RELEASED");
} else if (event->type == SDL_EVENT_JOYSTICK_BATTERY_UPDATED) {
const SDL_JoystickID which = event->jbattery.which;
add_message(which, "Joystick #%u battery -> %s - %d%%", (unsigned int) which, battery_state_string(event->jbattery.state), event->jbattery.percent);
}
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)
{
const Uint64 now = SDL_GetTicks();
const float msg_lifetime = 3500.0f; /* milliseconds a message lives for. */
EventMessage *msg = messages.next;
float prev_y = 0.0f;
int winw = 640, winh = 480;
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
SDL_RenderClear(renderer);
SDL_GetWindowSize(window, &winw, &winh);
while (msg) {
float x, y;
const float life_percent = ((float) (now - msg->start_ticks)) / msg_lifetime;
if (life_percent >= 1.0f) { /* msg is done. */
messages.next = msg->next;
if (messages_tail == msg) {
messages_tail = &messages;
}
SDL_free(msg->str);
SDL_free(msg);
msg = messages.next;
continue;
}
x = (((float) winw) - (SDL_strlen(msg->str) * SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE)) / 2.0f;
y = ((float) winh) * life_percent;
if ((prev_y != 0.0f) && ((prev_y - y) < ((float) SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE))) {
msg->start_ticks = now;
break; // wait for the previous message to tick up a little.
}
SDL_SetRenderDrawColor(renderer, msg->color.r, msg->color.g, msg->color.b, (Uint8) (((float) msg->color.a) * (1.0f - life_percent)));
SDL_RenderDebugText(renderer, x, y, msg->str);
prev_y = y;
msg = msg->next;
}
SDL_RenderPresent(renderer);
return SDL_APP_CONTINUE; /* carry on with the program! */
}
/* This function runs once at shutdown. */
void SDL_AppQuit(void *appstate, SDL_AppResult result)
{
/* SDL will clean up the window/renderer for us. We let the joysticks leak. */
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

View 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.

View file

@ -0,0 +1,121 @@
/*
* 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;
static float tilt_x = 0.0f;
static float tilt_y = 0.0f;
/* This function runs once at startup. */
SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[])
{
int w, h;
SDL_SetAppMetadata("Example Pen Drawing Lines", "1.0", "com.example.pen-drawing-lines");
if (!SDL_Init(SDL_INIT_VIDEO)) {
SDL_Log("Couldn't initialize SDL: %s", SDL_GetError());
return SDL_APP_FAILURE;
}
if (!SDL_CreateWindowAndRenderer("examples/pen/drawing-lines", 640, 480, 0, &window, &renderer)) {
SDL_Log("Couldn't create window/renderer: %s", SDL_GetError());
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. */
/* make sure the render target matches output size (for hidpi displays, etc) so drawing matches the pen's position on a tablet display. */
SDL_GetRenderOutputSize(renderer, &w, &h);
render_target = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, w, h);
if (!render_target) {
SDL_Log("Couldn't create render target: %s", SDL_GetError());
return SDL_APP_FAILURE;
}
/* just blank the render target to gray to start. */
SDL_SetRenderTarget(renderer, render_target);
SDL_SetRenderDrawColor(renderer, 100, 100, 100, SDL_ALPHA_OPAQUE);
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. */
} else if(event->paxis.axis == SDL_PEN_AXIS_XTILT) {
tilt_x = event->paxis.value;
} else if(event->paxis.axis == SDL_PEN_AXIS_YTILT) {
tilt_y = event->paxis.value;
}
}
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)
{
char debug_text[1024];
/* make sure we're drawing to the window and not the render target */
SDL_SetRenderTarget(renderer, NULL);
SDL_SetRenderDrawColor(renderer, 0, 0, 0, SDL_ALPHA_OPAQUE);
SDL_RenderClear(renderer); /* just in case. */
SDL_RenderTexture(renderer, render_target, NULL, NULL);
SDL_snprintf(debug_text, sizeof(debug_text), "Tilt: %f %f", tilt_x, tilt_y);
SDL_RenderDebugText(renderer, 0, 8, debug_text);
SDL_RenderPresent(renderer);
return SDL_APP_CONTINUE; /* carry on with the program! */
}
/* This function runs once at shutdown. */
void SDL_AppQuit(void *appstate, SDL_AppResult result)
{
SDL_DestroyTexture(render_target);
/* SDL will clean up the window/renderer for us. */
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2 KiB

View 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.

View file

@ -0,0 +1,68 @@
/*
* 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;
/* This function runs once at startup. */
SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[])
{
SDL_SetAppMetadata("Example Renderer Clear", "1.0", "com.example.renderer-clear");
if (!SDL_Init(SDL_INIT_VIDEO)) {
SDL_Log("Couldn't initialize SDL: %s", SDL_GetError());
return SDL_APP_FAILURE;
}
if (!SDL_CreateWindowAndRenderer("examples/renderer/clear", 640, 480, 0, &window, &renderer)) {
SDL_Log("Couldn't create window/renderer: %s", SDL_GetError());
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)
{
const double now = ((double)SDL_GetTicks()) / 1000.0; /* convert from milliseconds to seconds. */
/* choose the color for the frame we will draw. The sine wave trick makes it fade between colors smoothly. */
const float red = (float) (0.5 + 0.5 * SDL_sin(now));
const float green = (float) (0.5 + 0.5 * SDL_sin(now + SDL_PI_D * 2 / 3));
const float blue = (float) (0.5 + 0.5 * SDL_sin(now + SDL_PI_D * 4 / 3));
SDL_SetRenderDrawColorFloat(renderer, red, green, blue, SDL_ALPHA_OPAQUE_FLOAT); /* new color, full alpha. */
/* clear the window to the draw color. */
SDL_RenderClear(renderer);
/* put the newly-cleared rendering on the screen. */
SDL_RenderPresent(renderer);
return SDL_APP_CONTINUE; /* carry on with the program! */
}
/* This function runs once at shutdown. */
void SDL_AppQuit(void *appstate, SDL_AppResult result)
{
/* SDL will clean up the window/renderer for us. */
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2 KiB

View file

@ -0,0 +1,7 @@
This example creates an SDL window and renderer, and then draws some lines,
rectangles and points to it every frame.
This is just a quick overview of simple drawing primitives; futher examples
will explore them in more detail.

View file

@ -0,0 +1,95 @@
/*
* 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;
SDL_SetAppMetadata("Example Renderer Primitives", "1.0", "com.example.renderer-primitives");
if (!SDL_Init(SDL_INIT_VIDEO)) {
SDL_Log("Couldn't initialize SDL: %s", SDL_GetError());
return SDL_APP_FAILURE;
}
if (!SDL_CreateWindowAndRenderer("examples/renderer/primitives", 640, 480, 0, &window, &renderer)) {
SDL_Log("Couldn't create window/renderer: %s", SDL_GetError());
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, 33, 33, 33, SDL_ALPHA_OPAQUE); /* dark gray, 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, SDL_ALPHA_OPAQUE); /* 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, SDL_ALPHA_OPAQUE); /* 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, SDL_ALPHA_OPAQUE); /* 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, SDL_ALPHA_OPAQUE); /* 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_AppResult result)
{
/* SDL will clean up the window/renderer for us. */
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

View file

@ -0,0 +1,3 @@
This example creates an SDL window and renderer, and then draws a something
roughly like a Christmas tree with nothing but lines, every frame.

View file

@ -0,0 +1,93 @@
/*
* This example creates an SDL window and renderer, and then draws some lines
* 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;
/* This function runs once at startup. */
SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[])
{
SDL_SetAppMetadata("Example Renderer Lines", "1.0", "com.example.renderer-lines");
if (!SDL_Init(SDL_INIT_VIDEO)) {
SDL_Log("Couldn't initialize SDL: %s", SDL_GetError());
return SDL_APP_FAILURE;
}
if (!SDL_CreateWindowAndRenderer("examples/renderer/lines", 640, 480, 0, &window, &renderer)) {
SDL_Log("Couldn't create window/renderer: %s", SDL_GetError());
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)
{
int i;
/* Lines (line segments, really) are drawn in terms of points: a set of
X and Y coordinates, one set for each end of the line.
(0, 0) is the top left of the window, and larger numbers go down
and to the right. This isn't how geometry works, but this is pretty
standard in 2D graphics. */
static const SDL_FPoint line_points[] = {
{ 100, 354 }, { 220, 230 }, { 140, 230 }, { 320, 100 }, { 500, 230 },
{ 420, 230 }, { 540, 354 }, { 400, 354 }, { 100, 354 }
};
/* as you can see from this, rendering draws over whatever was drawn before it. */
SDL_SetRenderDrawColor(renderer, 100, 100, 100, SDL_ALPHA_OPAQUE); /* grey, full alpha */
SDL_RenderClear(renderer); /* start with a blank canvas. */
/* You can draw lines, one at a time, like these brown ones... */
SDL_SetRenderDrawColor(renderer, 127, 49, 32, SDL_ALPHA_OPAQUE);
SDL_RenderLine(renderer, 240, 450, 400, 450);
SDL_RenderLine(renderer, 240, 356, 400, 356);
SDL_RenderLine(renderer, 240, 356, 240, 450);
SDL_RenderLine(renderer, 400, 356, 400, 450);
/* You can also draw a series of connected lines in a single batch... */
SDL_SetRenderDrawColor(renderer, 0, 255, 0, SDL_ALPHA_OPAQUE);
SDL_RenderLines(renderer, line_points, SDL_arraysize(line_points));
/* here's a bunch of lines drawn out from a center point in a circle. */
/* we randomize the color of each line, so it functions as animation. */
for (i = 0; i < 360; i++) {
const float size = 30.0f;
const float x = 320.0f;
const float y = 95.0f - (size / 2.0f);
SDL_SetRenderDrawColor(renderer, SDL_rand(256), SDL_rand(256), SDL_rand(256), SDL_ALPHA_OPAQUE);
SDL_RenderLine(renderer, x, y, x + SDL_sinf((float) i) * size, y + SDL_cosf((float) i) * size);
}
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_AppResult result)
{
/* SDL will clean up the window/renderer for us. */
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

View file

@ -0,0 +1,3 @@
This example creates an SDL window and renderer, and then draws a bunch of
single points, moving across the screen.

Binary file not shown.

After

Width:  |  Height:  |  Size: 152 KiB

View file

@ -0,0 +1,118 @@
/*
* This example creates an SDL window and renderer, and then draws some 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 Uint64 last_time = 0;
#define WINDOW_WIDTH 640
#define WINDOW_HEIGHT 480
#define NUM_POINTS 500
#define MIN_PIXELS_PER_SECOND 30 /* move at least this many pixels per second. */
#define MAX_PIXELS_PER_SECOND 60 /* move this many pixels per second at most. */
/* (track everything as parallel arrays instead of a array of structs,
so we can pass the coordinates to the renderer in a single function call.) */
/* Points are plotted as a set of X and Y coordinates.
(0, 0) is the top left of the window, and larger numbers go down
and to the right. This isn't how geometry works, but this is pretty
standard in 2D graphics. */
static SDL_FPoint points[NUM_POINTS];
static float point_speeds[NUM_POINTS];
/* This function runs once at startup. */
SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[])
{
int i;
SDL_SetAppMetadata("Example Renderer Points", "1.0", "com.example.renderer-points");
if (!SDL_Init(SDL_INIT_VIDEO)) {
SDL_Log("Couldn't initialize SDL: %s", SDL_GetError());
return SDL_APP_FAILURE;
}
if (!SDL_CreateWindowAndRenderer("examples/renderer/points", WINDOW_WIDTH, WINDOW_HEIGHT, 0, &window, &renderer)) {
SDL_Log("Couldn't create window/renderer: %s", SDL_GetError());
return SDL_APP_FAILURE;
}
/* set up the data for a bunch of points. */
for (i = 0; i < SDL_arraysize(points); i++) {
points[i].x = SDL_randf() * ((float) WINDOW_WIDTH);
points[i].y = SDL_randf() * ((float) WINDOW_HEIGHT);
point_speeds[i] = MIN_PIXELS_PER_SECOND + (SDL_randf() * (MAX_PIXELS_PER_SECOND - MIN_PIXELS_PER_SECOND));
}
last_time = SDL_GetTicks();
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)
{
const Uint64 now = SDL_GetTicks();
const float elapsed = ((float) (now - last_time)) / 1000.0f; /* seconds since last iteration */
int i;
/* let's move all our points a little for a new frame. */
for (i = 0; i < SDL_arraysize(points); i++) {
const float distance = elapsed * point_speeds[i];
points[i].x += distance;
points[i].y += distance;
if ((points[i].x >= WINDOW_WIDTH) || (points[i].y >= WINDOW_HEIGHT)) {
/* off the screen; restart it elsewhere! */
if (SDL_rand(2)) {
points[i].x = SDL_randf() * ((float) WINDOW_WIDTH);
points[i].y = 0.0f;
} else {
points[i].x = 0.0f;
points[i].y = SDL_randf() * ((float) WINDOW_HEIGHT);
}
point_speeds[i] = MIN_PIXELS_PER_SECOND + (SDL_randf() * (MAX_PIXELS_PER_SECOND - MIN_PIXELS_PER_SECOND));
}
}
last_time = now;
/* as you can see from this, rendering draws over whatever was drawn before it. */
SDL_SetRenderDrawColor(renderer, 0, 0, 0, SDL_ALPHA_OPAQUE); /* black, full alpha */
SDL_RenderClear(renderer); /* start with a blank canvas. */
SDL_SetRenderDrawColor(renderer, 255, 255, 255, SDL_ALPHA_OPAQUE); /* white, full alpha */
SDL_RenderPoints(renderer, points, SDL_arraysize(points)); /* draw all the points! */
/* You can also draw single points with SDL_RenderPoint(), but it's
cheaper (sometimes significantly so) to do them all at once. */
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_AppResult result)
{
/* SDL will clean up the window/renderer for us. */
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

View file

@ -0,0 +1,3 @@
This example creates an SDL window and renderer, and then draws a few
rectangles that change size each frame.

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

View file

@ -0,0 +1,112 @@
/*
* This example creates an SDL window and renderer, and then draws some
* rectangles 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;
#define WINDOW_WIDTH 640
#define WINDOW_HEIGHT 480
/* This function runs once at startup. */
SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[])
{
SDL_SetAppMetadata("Example Renderer Rectangles", "1.0", "com.example.renderer-rectangles");
if (!SDL_Init(SDL_INIT_VIDEO)) {
SDL_Log("Couldn't initialize SDL: %s", SDL_GetError());
return SDL_APP_FAILURE;
}
if (!SDL_CreateWindowAndRenderer("examples/renderer/rectangles", WINDOW_WIDTH, WINDOW_HEIGHT, 0, &window, &renderer)) {
SDL_Log("Couldn't create window/renderer: %s", SDL_GetError());
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)
{
SDL_FRect rects[16];
const Uint64 now = SDL_GetTicks();
int i;
/* we'll have the rectangles grow and shrink over a few seconds. */
const float direction = ((now % 2000) >= 1000) ? 1.0f : -1.0f;
const float scale = ((float) (((int) (now % 1000)) - 500) / 500.0f) * direction;
/* as you can see from this, rendering draws over whatever was drawn before it. */
SDL_SetRenderDrawColor(renderer, 0, 0, 0, SDL_ALPHA_OPAQUE); /* black, full alpha */
SDL_RenderClear(renderer); /* start with a blank canvas. */
/* Rectangles are comprised of set of X and Y coordinates, plus width and
height. (0, 0) is the top left of the window, and larger numbers go
down and to the right. This isn't how geometry works, but this is
pretty standard in 2D graphics. */
/* Let's draw a single rectangle (square, really). */
rects[0].x = rects[0].y = 100;
rects[0].w = rects[0].h = 100 + (100 * scale);
SDL_SetRenderDrawColor(renderer, 255, 0, 0, SDL_ALPHA_OPAQUE); /* red, full alpha */
SDL_RenderRect(renderer, &rects[0]);
/* Now let's draw several rectangles with one function call. */
for (i = 0; i < 3; i++) {
const float size = (i+1) * 50.0f;
rects[i].w = rects[i].h = size + (size * scale);
rects[i].x = (WINDOW_WIDTH - rects[i].w) / 2; /* center it. */
rects[i].y = (WINDOW_HEIGHT - rects[i].h) / 2; /* center it. */
}
SDL_SetRenderDrawColor(renderer, 0, 255, 0, SDL_ALPHA_OPAQUE); /* green, full alpha */
SDL_RenderRects(renderer, rects, 3); /* draw three rectangles at once */
/* those were rectangle _outlines_, really. You can also draw _filled_ rectangles! */
rects[0].x = 400;
rects[0].y = 50;
rects[0].w = 100 + (100 * scale);
rects[0].h = 50 + (50 * scale);
SDL_SetRenderDrawColor(renderer, 0, 0, 255, SDL_ALPHA_OPAQUE); /* blue, full alpha */
SDL_RenderFillRect(renderer, &rects[0]);
/* ...and also fill a bunch of rectangles at once... */
for (i = 0; i < SDL_arraysize(rects); i++) {
const float w = (float) (WINDOW_WIDTH / SDL_arraysize(rects));
const float h = i * 8.0f;
rects[i].x = i * w;
rects[i].y = WINDOW_HEIGHT - h;
rects[i].w = w;
rects[i].h = h;
}
SDL_SetRenderDrawColor(renderer, 255, 255, 255, SDL_ALPHA_OPAQUE); /* white, full alpha */
SDL_RenderFillRects(renderer, rects, SDL_arraysize(rects));
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_AppResult result)
{
/* SDL will clean up the window/renderer for us. */
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

View file

@ -0,0 +1,3 @@
This example creates an SDL window and renderer, loads a texture from a
.bmp file, and then draws it a few times each frame.

Binary file not shown.

After

Width:  |  Height:  |  Size: 592 KiB

View file

@ -0,0 +1,127 @@
/*
* This example creates an SDL window and renderer, and then draws some
* textures 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_Texture *texture = NULL;
static int texture_width = 0;
static int texture_height = 0;
#define WINDOW_WIDTH 640
#define WINDOW_HEIGHT 480
/* This function runs once at startup. */
SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[])
{
SDL_Surface *surface = NULL;
char *bmp_path = NULL;
SDL_SetAppMetadata("Example Renderer Textures", "1.0", "com.example.renderer-textures");
if (!SDL_Init(SDL_INIT_VIDEO)) {
SDL_Log("Couldn't initialize SDL: %s", SDL_GetError());
return SDL_APP_FAILURE;
}
if (!SDL_CreateWindowAndRenderer("examples/renderer/textures", WINDOW_WIDTH, WINDOW_HEIGHT, 0, &window, &renderer)) {
SDL_Log("Couldn't create window/renderer: %s", SDL_GetError());
return SDL_APP_FAILURE;
}
/* Textures are pixel data that we upload to the video hardware for fast drawing. Lots of 2D
engines refer to these as "sprites." We'll do a static texture (upload once, draw many
times) with data from a bitmap file. */
/* SDL_Surface is pixel data the CPU can access. SDL_Texture is pixel data the GPU can access.
Load a .bmp into a surface, move it to a texture from there. */
SDL_asprintf(&bmp_path, "%ssample.bmp", SDL_GetBasePath()); /* allocate a string of the full file path */
surface = SDL_LoadBMP(bmp_path);
if (!surface) {
SDL_Log("Couldn't load bitmap: %s", SDL_GetError());
return SDL_APP_FAILURE;
}
SDL_free(bmp_path); /* done with this, the file is loaded. */
texture_width = surface->w;
texture_height = surface->h;
texture = SDL_CreateTextureFromSurface(renderer, surface);
if (!texture) {
SDL_Log("Couldn't create static texture: %s", SDL_GetError());
return SDL_APP_FAILURE;
}
SDL_DestroySurface(surface); /* done with this, the texture has a copy of the pixels now. */
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 dst_rect;
const Uint64 now = SDL_GetTicks();
/* we'll have some textures move around over a few seconds. */
const float direction = ((now % 2000) >= 1000) ? 1.0f : -1.0f;
const float scale = ((float) (((int) (now % 1000)) - 500) / 500.0f) * direction;
/* as you can see from this, rendering draws over whatever was drawn before it. */
SDL_SetRenderDrawColor(renderer, 0, 0, 0, SDL_ALPHA_OPAQUE); /* black, full alpha */
SDL_RenderClear(renderer); /* start with a blank canvas. */
/* Just draw the static texture a few times. You can think of it like a
stamp, there isn't a limit to the number of times you can draw with it. */
/* top left */
dst_rect.x = (100.0f * scale);
dst_rect.y = 0.0f;
dst_rect.w = (float) texture_width;
dst_rect.h = (float) texture_height;
SDL_RenderTexture(renderer, texture, NULL, &dst_rect);
/* center this one. */
dst_rect.x = ((float) (WINDOW_WIDTH - texture_width)) / 2.0f;
dst_rect.y = ((float) (WINDOW_HEIGHT - texture_height)) / 2.0f;
dst_rect.w = (float) texture_width;
dst_rect.h = (float) texture_height;
SDL_RenderTexture(renderer, texture, NULL, &dst_rect);
/* bottom right. */
dst_rect.x = ((float) (WINDOW_WIDTH - texture_width)) - (100.0f * scale);
dst_rect.y = (float) (WINDOW_HEIGHT - texture_height);
dst_rect.w = (float) texture_width;
dst_rect.h = (float) texture_height;
SDL_RenderTexture(renderer, texture, NULL, &dst_rect);
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_AppResult result)
{
SDL_DestroyTexture(texture);
/* SDL will clean up the window/renderer for us. */
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 233 KiB

View file

@ -0,0 +1,3 @@
This example creates an SDL window and renderer, then a streaming texture that
it will update every frame before drawing it to the screen.

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View file

@ -0,0 +1,109 @@
/*
* This example creates an SDL window and renderer, and then draws a streaming
* texture 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_Texture *texture = NULL;
#define TEXTURE_SIZE 150
#define WINDOW_WIDTH 640
#define WINDOW_HEIGHT 480
/* This function runs once at startup. */
SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[])
{
SDL_SetAppMetadata("Example Renderer Streaming Textures", "1.0", "com.example.renderer-streaming-textures");
if (!SDL_Init(SDL_INIT_VIDEO)) {
SDL_Log("Couldn't initialize SDL: %s", SDL_GetError());
return SDL_APP_FAILURE;
}
if (!SDL_CreateWindowAndRenderer("examples/renderer/streaming-textures", WINDOW_WIDTH, WINDOW_HEIGHT, 0, &window, &renderer)) {
SDL_Log("Couldn't create window/renderer: %s", SDL_GetError());
return SDL_APP_FAILURE;
}
texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_STREAMING, TEXTURE_SIZE, TEXTURE_SIZE);
if (!texture) {
SDL_Log("Couldn't create streaming texture: %s", SDL_GetError());
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)
{
SDL_FRect dst_rect;
const Uint64 now = SDL_GetTicks();
SDL_Surface *surface = NULL;
/* we'll have some color move around over a few seconds. */
const float direction = ((now % 2000) >= 1000) ? 1.0f : -1.0f;
const float scale = ((float) (((int) (now % 1000)) - 500) / 500.0f) * direction;
/* To update a streaming texture, you need to lock it first. This gets you access to the pixels.
Note that this is considered a _write-only_ operation: the buffer you get from locking
might not acutally have the existing contents of the texture, and you have to write to every
locked pixel! */
/* You can use SDL_LockTexture() to get an array of raw pixels, but we're going to use
SDL_LockTextureToSurface() here, because it wraps that array in a temporary SDL_Surface,
letting us use the surface drawing functions instead of lighting up individual pixels. */
if (SDL_LockTextureToSurface(texture, NULL, &surface)) {
SDL_Rect r;
SDL_FillSurfaceRect(surface, NULL, SDL_MapRGB(SDL_GetPixelFormatDetails(surface->format), NULL, 0, 0, 0)); /* make the whole surface black */
r.w = TEXTURE_SIZE;
r.h = TEXTURE_SIZE / 10;
r.x = 0;
r.y = (int) (((float) (TEXTURE_SIZE - r.h)) * ((scale + 1.0f) / 2.0f));
SDL_FillSurfaceRect(surface, &r, SDL_MapRGB(SDL_GetPixelFormatDetails(surface->format), NULL, 0, 255, 0)); /* make a strip of the surface green */
SDL_UnlockTexture(texture); /* upload the changes (and frees the temporary surface)! */
}
/* as you can see from this, rendering draws over whatever was drawn before it. */
SDL_SetRenderDrawColor(renderer, 66, 66, 66, SDL_ALPHA_OPAQUE); /* grey, full alpha */
SDL_RenderClear(renderer); /* start with a blank canvas. */
/* Just draw the static texture a few times. You can think of it like a
stamp, there isn't a limit to the number of times you can draw with it. */
/* Center this one. It'll draw the latest version of the texture we drew while it was locked. */
dst_rect.x = ((float) (WINDOW_WIDTH - TEXTURE_SIZE)) / 2.0f;
dst_rect.y = ((float) (WINDOW_HEIGHT - TEXTURE_SIZE)) / 2.0f;
dst_rect.w = dst_rect.h = (float) TEXTURE_SIZE;
SDL_RenderTexture(renderer, texture, NULL, &dst_rect);
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_AppResult result)
{
SDL_DestroyTexture(texture);
/* SDL will clean up the window/renderer for us. */
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

View file

@ -0,0 +1,3 @@
This example creates an SDL window and renderer, loads a texture from a .bmp
file, and then draws it, rotating around the center of the screen.

Binary file not shown.

After

Width:  |  Height:  |  Size: 306 KiB

View file

@ -0,0 +1,113 @@
/*
* This example creates an SDL window and renderer, and then draws some
* rotated textures 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_Texture *texture = NULL;
static int texture_width = 0;
static int texture_height = 0;
#define WINDOW_WIDTH 640
#define WINDOW_HEIGHT 480
/* This function runs once at startup. */
SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[])
{
SDL_Surface *surface = NULL;
char *bmp_path = NULL;
SDL_SetAppMetadata("Example Renderer Rotating Textures", "1.0", "com.example.renderer-rotating-textures");
if (!SDL_Init(SDL_INIT_VIDEO)) {
SDL_Log("Couldn't initialize SDL: %s", SDL_GetError());
return SDL_APP_FAILURE;
}
if (!SDL_CreateWindowAndRenderer("examples/renderer/rotating-textures", WINDOW_WIDTH, WINDOW_HEIGHT, 0, &window, &renderer)) {
SDL_Log("Couldn't create window/renderer: %s", SDL_GetError());
return SDL_APP_FAILURE;
}
/* Textures are pixel data that we upload to the video hardware for fast drawing. Lots of 2D
engines refer to these as "sprites." We'll do a static texture (upload once, draw many
times) with data from a bitmap file. */
/* SDL_Surface is pixel data the CPU can access. SDL_Texture is pixel data the GPU can access.
Load a .bmp into a surface, move it to a texture from there. */
SDL_asprintf(&bmp_path, "%ssample.bmp", SDL_GetBasePath()); /* allocate a string of the full file path */
surface = SDL_LoadBMP(bmp_path);
if (!surface) {
SDL_Log("Couldn't load bitmap: %s", SDL_GetError());
return SDL_APP_FAILURE;
}
SDL_free(bmp_path); /* done with this, the file is loaded. */
texture_width = surface->w;
texture_height = surface->h;
texture = SDL_CreateTextureFromSurface(renderer, surface);
if (!texture) {
SDL_Log("Couldn't create static texture: %s", SDL_GetError());
return SDL_APP_FAILURE;
}
SDL_DestroySurface(surface); /* done with this, the texture has a copy of the pixels now. */
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_FPoint center;
SDL_FRect dst_rect;
const Uint64 now = SDL_GetTicks();
/* we'll have a texture rotate around over 2 seconds (2000 milliseconds). 360 degrees in a circle! */
const float rotation = (((float) ((int) (now % 2000))) / 2000.0f) * 360.0f;
/* as you can see from this, rendering draws over whatever was drawn before it. */
SDL_SetRenderDrawColor(renderer, 0, 0, 0, SDL_ALPHA_OPAQUE); /* black, full alpha */
SDL_RenderClear(renderer); /* start with a blank canvas. */
/* Center this one, and draw it with some rotation so it spins! */
dst_rect.x = ((float) (WINDOW_WIDTH - texture_width)) / 2.0f;
dst_rect.y = ((float) (WINDOW_HEIGHT - texture_height)) / 2.0f;
dst_rect.w = (float) texture_width;
dst_rect.h = (float) texture_height;
/* rotate it around the center of the texture; you can rotate it from a different point, too! */
center.x = texture_width / 2.0f;
center.y = texture_height / 2.0f;
SDL_RenderTextureRotated(renderer, texture, NULL, &dst_rect, rotation, &center, SDL_FLIP_NONE);
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_AppResult result)
{
SDL_DestroyTexture(texture);
/* SDL will clean up the window/renderer for us. */
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

View file

@ -0,0 +1,3 @@
This example creates an SDL window and renderer, loads a texture from a .bmp
file, and then draws it, scaling it up and down.

Binary file not shown.

After

Width:  |  Height:  |  Size: 311 KiB

View file

@ -0,0 +1,110 @@
/*
* This example creates an SDL window and renderer, and then draws some
* textures 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_Texture *texture = NULL;
static int texture_width = 0;
static int texture_height = 0;
#define WINDOW_WIDTH 640
#define WINDOW_HEIGHT 480
/* This function runs once at startup. */
SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[])
{
SDL_Surface *surface = NULL;
char *bmp_path = NULL;
SDL_SetAppMetadata("Example Renderer Scaling Textures", "1.0", "com.example.renderer-scaling-textures");
if (!SDL_Init(SDL_INIT_VIDEO)) {
SDL_Log("Couldn't initialize SDL: %s", SDL_GetError());
return SDL_APP_FAILURE;
}
if (!SDL_CreateWindowAndRenderer("examples/renderer/scaling-textures", WINDOW_WIDTH, WINDOW_HEIGHT, 0, &window, &renderer)) {
SDL_Log("Couldn't create window/renderer: %s", SDL_GetError());
return SDL_APP_FAILURE;
}
/* Textures are pixel data that we upload to the video hardware for fast drawing. Lots of 2D
engines refer to these as "sprites." We'll do a static texture (upload once, draw many
times) with data from a bitmap file. */
/* SDL_Surface is pixel data the CPU can access. SDL_Texture is pixel data the GPU can access.
Load a .bmp into a surface, move it to a texture from there. */
SDL_asprintf(&bmp_path, "%ssample.bmp", SDL_GetBasePath()); /* allocate a string of the full file path */
surface = SDL_LoadBMP(bmp_path);
if (!surface) {
SDL_Log("Couldn't load bitmap: %s", SDL_GetError());
return SDL_APP_FAILURE;
}
SDL_free(bmp_path); /* done with this, the file is loaded. */
texture_width = surface->w;
texture_height = surface->h;
texture = SDL_CreateTextureFromSurface(renderer, surface);
if (!texture) {
SDL_Log("Couldn't create static texture: %s", SDL_GetError());
return SDL_APP_FAILURE;
}
SDL_DestroySurface(surface); /* done with this, the texture has a copy of the pixels now. */
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 dst_rect;
const Uint64 now = SDL_GetTicks();
/* we'll have the texture grow and shrink over a few seconds. */
const float direction = ((now % 2000) >= 1000) ? 1.0f : -1.0f;
const float scale = ((float) (((int) (now % 1000)) - 500) / 500.0f) * direction;
/* as you can see from this, rendering draws over whatever was drawn before it. */
SDL_SetRenderDrawColor(renderer, 0, 0, 0, SDL_ALPHA_OPAQUE); /* black, full alpha */
SDL_RenderClear(renderer); /* start with a blank canvas. */
/* center this one and make it grow and shrink. */
dst_rect.w = (float) texture_width + (texture_width * scale);
dst_rect.h = (float) texture_height + (texture_height * scale);
dst_rect.x = (WINDOW_WIDTH - dst_rect.w) / 2.0f;
dst_rect.y = (WINDOW_HEIGHT - dst_rect.h) / 2.0f;
SDL_RenderTexture(renderer, texture, NULL, &dst_rect);
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_AppResult result)
{
SDL_DestroyTexture(texture);
/* SDL will clean up the window/renderer for us. */
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

View file

@ -0,0 +1,3 @@
This example creates an SDL window and renderer, loads a texture from a .bmp
file, and then draws geometry (arbitrary polygons) using it.

View file

@ -0,0 +1,166 @@
/*
* This example creates an SDL window and renderer, and then draws some
* geometry (arbitrary polygons) 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_Texture *texture = NULL;
static int texture_width = 0;
static int texture_height = 0;
#define WINDOW_WIDTH 640
#define WINDOW_HEIGHT 480
/* This function runs once at startup. */
SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[])
{
SDL_Surface *surface = NULL;
char *bmp_path = NULL;
SDL_SetAppMetadata("Example Renderer Geometry", "1.0", "com.example.renderer-geometry");
if (!SDL_Init(SDL_INIT_VIDEO)) {
SDL_Log("Couldn't initialize SDL: %s", SDL_GetError());
return SDL_APP_FAILURE;
}
if (!SDL_CreateWindowAndRenderer("examples/renderer/geometry", WINDOW_WIDTH, WINDOW_HEIGHT, 0, &window, &renderer)) {
SDL_Log("Couldn't create window/renderer: %s", SDL_GetError());
return SDL_APP_FAILURE;
}
/* Textures are pixel data that we upload to the video hardware for fast drawing. Lots of 2D
engines refer to these as "sprites." We'll do a static texture (upload once, draw many
times) with data from a bitmap file. */
/* SDL_Surface is pixel data the CPU can access. SDL_Texture is pixel data the GPU can access.
Load a .bmp into a surface, move it to a texture from there. */
SDL_asprintf(&bmp_path, "%ssample.bmp", SDL_GetBasePath()); /* allocate a string of the full file path */
surface = SDL_LoadBMP(bmp_path);
if (!surface) {
SDL_Log("Couldn't load bitmap: %s", SDL_GetError());
return SDL_APP_FAILURE;
}
SDL_free(bmp_path); /* done with this, the file is loaded. */
texture_width = surface->w;
texture_height = surface->h;
texture = SDL_CreateTextureFromSurface(renderer, surface);
if (!texture) {
SDL_Log("Couldn't create static texture: %s", SDL_GetError());
return SDL_APP_FAILURE;
}
SDL_DestroySurface(surface); /* done with this, the texture has a copy of the pixels now. */
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)
{
const Uint64 now = SDL_GetTicks();
/* we'll have the triangle grow and shrink over a few seconds. */
const float direction = ((now % 2000) >= 1000) ? 1.0f : -1.0f;
const float scale = ((float) (((int) (now % 1000)) - 500) / 500.0f) * direction;
const float size = 200.0f + (200.0f * scale);
SDL_Vertex vertices[4];
int i;
/* as you can see from this, rendering draws over whatever was drawn before it. */
SDL_SetRenderDrawColor(renderer, 0, 0, 0, SDL_ALPHA_OPAQUE); /* black, full alpha */
SDL_RenderClear(renderer); /* start with a blank canvas. */
/* Draw a single triangle with a different color at each vertex. Center this one and make it grow and shrink. */
/* You always draw triangles with this, but you can string triangles together to form polygons. */
SDL_zeroa(vertices);
vertices[0].position.x = ((float) WINDOW_WIDTH) / 2.0f;
vertices[0].position.y = (((float) WINDOW_HEIGHT) - size) / 2.0f;
vertices[0].color.r = 1.0f;
vertices[0].color.a = 1.0f;
vertices[1].position.x = (((float) WINDOW_WIDTH) + size) / 2.0f;
vertices[1].position.y = (((float) WINDOW_HEIGHT) + size) / 2.0f;
vertices[1].color.g = 1.0f;
vertices[1].color.a = 1.0f;
vertices[2].position.x = (((float) WINDOW_WIDTH) - size) / 2.0f;
vertices[2].position.y = (((float) WINDOW_HEIGHT) + size) / 2.0f;
vertices[2].color.b = 1.0f;
vertices[2].color.a = 1.0f;
SDL_RenderGeometry(renderer, NULL, vertices, 3, NULL, 0);
/* you can also map a texture to the geometry! Texture coordinates go from 0.0f to 1.0f. That will be the location
in the texture bound to this vertex. */
SDL_zeroa(vertices);
vertices[0].position.x = 10.0f;
vertices[0].position.y = 10.0f;
vertices[0].color.r = vertices[0].color.g = vertices[0].color.b = vertices[0].color.a = 1.0f;
vertices[0].tex_coord.x = 0.0f;
vertices[0].tex_coord.y = 0.0f;
vertices[1].position.x = 150.0f;
vertices[1].position.y = 10.0f;
vertices[1].color.r = vertices[1].color.g = vertices[1].color.b = vertices[1].color.a = 1.0f;
vertices[1].tex_coord.x = 1.0f;
vertices[1].tex_coord.y = 0.0f;
vertices[2].position.x = 10.0f;
vertices[2].position.y = 150.0f;
vertices[2].color.r = vertices[2].color.g = vertices[2].color.b = vertices[2].color.a = 1.0f;
vertices[2].tex_coord.x = 0.0f;
vertices[2].tex_coord.y = 1.0f;
SDL_RenderGeometry(renderer, texture, vertices, 3, NULL, 0);
/* Did that only draw half of the texture? You can do multiple triangles sharing some vertices,
using indices, to get the whole thing on the screen: */
/* Let's just move this over so it doesn't overlap... */
for (i = 0; i < 3; i++) {
vertices[i].position.x += 450;
}
/* we need one more vertex, since the two triangles can share two of them. */
vertices[3].position.x = 600.0f;
vertices[3].position.y = 150.0f;
vertices[3].color.r = vertices[3].color.g = vertices[3].color.b = vertices[3].color.a = 1.0f;
vertices[3].tex_coord.x = 1.0f;
vertices[3].tex_coord.y = 1.0f;
/* And an index to tell it to reuse some of the vertices between triangles... */
{
/* 4 vertices, but 6 actual places they used. Indices need less bandwidth to transfer and can reorder vertices easily! */
const int indices[] = { 0, 1, 2, 1, 2, 3 };
SDL_RenderGeometry(renderer, texture, vertices, 4, indices, SDL_arraysize(indices));
}
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_AppResult result)
{
SDL_DestroyTexture(texture);
/* SDL will clean up the window/renderer for us. */
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

View file

@ -0,0 +1,3 @@
This example creates an SDL window and renderer, loads a texture from a
.bmp file, and then draws it a few times each frame, adjusting the colors.

View file

@ -0,0 +1,134 @@
/*
* This example creates an SDL window and renderer, and then draws some
* textures to it every frame, adjusting their color.
*
* 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 *texture = NULL;
static int texture_width = 0;
static int texture_height = 0;
#define WINDOW_WIDTH 640
#define WINDOW_HEIGHT 480
/* This function runs once at startup. */
SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[])
{
SDL_Surface *surface = NULL;
char *bmp_path = NULL;
SDL_SetAppMetadata("Example Renderer Color Mods", "1.0", "com.example.renderer-color-mods");
if (!SDL_Init(SDL_INIT_VIDEO)) {
SDL_Log("Couldn't initialize SDL: %s", SDL_GetError());
return SDL_APP_FAILURE;
}
if (!SDL_CreateWindowAndRenderer("examples/renderer/color-mods", WINDOW_WIDTH, WINDOW_HEIGHT, 0, &window, &renderer)) {
SDL_Log("Couldn't create window/renderer: %s", SDL_GetError());
return SDL_APP_FAILURE;
}
/* Textures are pixel data that we upload to the video hardware for fast drawing. Lots of 2D
engines refer to these as "sprites." We'll do a static texture (upload once, draw many
times) with data from a bitmap file. */
/* SDL_Surface is pixel data the CPU can access. SDL_Texture is pixel data the GPU can access.
Load a .bmp into a surface, move it to a texture from there. */
SDL_asprintf(&bmp_path, "%ssample.bmp", SDL_GetBasePath()); /* allocate a string of the full file path */
surface = SDL_LoadBMP(bmp_path);
if (!surface) {
SDL_Log("Couldn't load bitmap: %s", SDL_GetError());
return SDL_APP_FAILURE;
}
SDL_free(bmp_path); /* done with this, the file is loaded. */
texture_width = surface->w;
texture_height = surface->h;
texture = SDL_CreateTextureFromSurface(renderer, surface);
if (!texture) {
SDL_Log("Couldn't create static texture: %s", SDL_GetError());
return SDL_APP_FAILURE;
}
SDL_DestroySurface(surface); /* done with this, the texture has a copy of the pixels now. */
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 dst_rect;
const double now = ((double)SDL_GetTicks()) / 1000.0; /* convert from milliseconds to seconds. */
/* choose the modulation values for the center texture. The sine wave trick makes it fade between colors smoothly. */
const float red = (float) (0.5 + 0.5 * SDL_sin(now));
const float green = (float) (0.5 + 0.5 * SDL_sin(now + SDL_PI_D * 2 / 3));
const float blue = (float) (0.5 + 0.5 * SDL_sin(now + SDL_PI_D * 4 / 3));
/* as you can see from this, rendering draws over whatever was drawn before it. */
SDL_SetRenderDrawColor(renderer, 0, 0, 0, SDL_ALPHA_OPAQUE); /* black, full alpha */
SDL_RenderClear(renderer); /* start with a blank canvas. */
/* Just draw the static texture a few times. You can think of it like a
stamp, there isn't a limit to the number of times you can draw with it. */
/* Color modulation multiplies each pixel's red, green, and blue intensities by the mod values,
so multiplying by 1.0f will leave a color intensity alone, 0.0f will shut off that color
completely, etc. */
/* top left; let's make this one blue! */
dst_rect.x = 0.0f;
dst_rect.y = 0.0f;
dst_rect.w = (float) texture_width;
dst_rect.h = (float) texture_height;
SDL_SetTextureColorModFloat(texture, 0.0f, 0.0f, 1.0f); /* kill all red and green. */
SDL_RenderTexture(renderer, texture, NULL, &dst_rect);
/* center this one, and have it cycle through red/green/blue modulations. */
dst_rect.x = ((float) (WINDOW_WIDTH - texture_width)) / 2.0f;
dst_rect.y = ((float) (WINDOW_HEIGHT - texture_height)) / 2.0f;
dst_rect.w = (float) texture_width;
dst_rect.h = (float) texture_height;
SDL_SetTextureColorModFloat(texture, red, green, blue);
SDL_RenderTexture(renderer, texture, NULL, &dst_rect);
/* bottom right; let's make this one red! */
dst_rect.x = (float) (WINDOW_WIDTH - texture_width);
dst_rect.y = (float) (WINDOW_HEIGHT - texture_height);
dst_rect.w = (float) texture_width;
dst_rect.h = (float) texture_height;
SDL_SetTextureColorModFloat(texture, 1.0f, 0.0f, 0.0f); /* kill all green and blue. */
SDL_RenderTexture(renderer, texture, NULL, &dst_rect);
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_AppResult result)
{
SDL_DestroyTexture(texture);
/* SDL will clean up the window/renderer for us. */
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 272 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 120 KiB

View file

@ -0,0 +1,4 @@
This example creates an SDL window and renderer, loads a texture
from a .bmp file, and then draws it a few times each frame, adjusting
the viewport before each draw.

Binary file not shown.

After

Width:  |  Height:  |  Size: 143 KiB

View file

@ -0,0 +1,136 @@
/*
* This example creates an SDL window and renderer, and then draws some
* textures to it every frame, adjusting the viewport.
*
* 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 *texture = NULL;
static int texture_width = 0;
static int texture_height = 0;
#define WINDOW_WIDTH 640
#define WINDOW_HEIGHT 480
/* This function runs once at startup. */
SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[])
{
SDL_Surface *surface = NULL;
char *bmp_path = NULL;
SDL_SetAppMetadata("Example Renderer Viewport", "1.0", "com.example.renderer-viewport");
if (!SDL_Init(SDL_INIT_VIDEO)) {
SDL_Log("Couldn't initialize SDL: %s", SDL_GetError());
return SDL_APP_FAILURE;
}
if (!SDL_CreateWindowAndRenderer("examples/renderer/viewport", WINDOW_WIDTH, WINDOW_HEIGHT, 0, &window, &renderer)) {
SDL_Log("Couldn't create window/renderer: %s", SDL_GetError());
return SDL_APP_FAILURE;
}
/* Textures are pixel data that we upload to the video hardware for fast drawing. Lots of 2D
engines refer to these as "sprites." We'll do a static texture (upload once, draw many
times) with data from a bitmap file. */
/* SDL_Surface is pixel data the CPU can access. SDL_Texture is pixel data the GPU can access.
Load a .bmp into a surface, move it to a texture from there. */
SDL_asprintf(&bmp_path, "%ssample.bmp", SDL_GetBasePath()); /* allocate a string of the full file path */
surface = SDL_LoadBMP(bmp_path);
if (!surface) {
SDL_Log("Couldn't load bitmap: %s", SDL_GetError());
return SDL_APP_FAILURE;
}
SDL_free(bmp_path); /* done with this, the file is loaded. */
texture_width = surface->w;
texture_height = surface->h;
texture = SDL_CreateTextureFromSurface(renderer, surface);
if (!texture) {
SDL_Log("Couldn't create static texture: %s", SDL_GetError());
return SDL_APP_FAILURE;
}
SDL_DestroySurface(surface); /* done with this, the texture has a copy of the pixels now. */
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 dst_rect = { 0, 0, (float) texture_width, (float) texture_height };
SDL_Rect viewport;
/* Setting a viewport has the effect of limiting the area that rendering
can happen, and making coordinate (0, 0) live somewhere else in the
window. It does _not_ scale rendering to fit the viewport. */
/* as you can see from this, rendering draws over whatever was drawn before it. */
SDL_SetRenderDrawColor(renderer, 0, 0, 0, SDL_ALPHA_OPAQUE); /* black, full alpha */
SDL_RenderClear(renderer); /* start with a blank canvas. */
/* Draw once with the whole window as the viewport. */
viewport.x = 0;
viewport.y = 0;
viewport.w = WINDOW_WIDTH / 2;
viewport.h = WINDOW_HEIGHT / 2;
SDL_SetRenderViewport(renderer, NULL); /* NULL means "use the whole window" */
SDL_RenderTexture(renderer, texture, NULL, &dst_rect);
/* top right quarter of the window. */
viewport.x = WINDOW_WIDTH / 2;
viewport.y = WINDOW_HEIGHT / 2;
viewport.w = WINDOW_WIDTH / 2;
viewport.h = WINDOW_HEIGHT / 2;
SDL_SetRenderViewport(renderer, &viewport);
SDL_RenderTexture(renderer, texture, NULL, &dst_rect);
/* bottom 20% of the window. Note it clips the width! */
viewport.x = 0;
viewport.y = WINDOW_HEIGHT - (WINDOW_HEIGHT / 5);
viewport.w = WINDOW_WIDTH / 5;
viewport.h = WINDOW_HEIGHT / 5;
SDL_SetRenderViewport(renderer, &viewport);
SDL_RenderTexture(renderer, texture, NULL, &dst_rect);
/* what happens if you try to draw above the viewport? It should clip! */
viewport.x = 100;
viewport.y = 200;
viewport.w = WINDOW_WIDTH;
viewport.h = WINDOW_HEIGHT;
SDL_SetRenderViewport(renderer, &viewport);
dst_rect.y = -50;
SDL_RenderTexture(renderer, texture, NULL, &dst_rect);
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_AppResult result)
{
SDL_DestroyTexture(texture);
/* SDL will clean up the window/renderer for us. */
}

View file

@ -0,0 +1,5 @@
This example creates an SDL window and renderer, loads a texture
from a .bmp file, and stretches it across the window. Each frame, we move
the clipping rectangle around, so only a small square of the texture is
actually drawn.

View file

@ -0,0 +1,137 @@
/*
* This example creates an SDL window and renderer, and then draws a scene
* to it every frame, while sliding around a clipping rectangle.
*
* 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 WINDOW_WIDTH 640
#define WINDOW_HEIGHT 480
#define CLIPRECT_SIZE 250
#define CLIPRECT_SPEED 200 /* pixels per second */
/* 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 *texture = NULL;
static SDL_FPoint cliprect_position;
static SDL_FPoint cliprect_direction;
static Uint64 last_time = 0;
/* A lot of this program is examples/renderer/02-primitives, so we have a good
visual that we can slide a clip rect around. The actual new magic in here
is the SDL_SetRenderClipRect() function. */
/* This function runs once at startup. */
SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[])
{
SDL_Surface *surface = NULL;
char *bmp_path = NULL;
SDL_SetAppMetadata("Example Renderer Clipping Rectangle", "1.0", "com.example.renderer-cliprect");
if (!SDL_Init(SDL_INIT_VIDEO)) {
SDL_Log("Couldn't initialize SDL: %s", SDL_GetError());
return SDL_APP_FAILURE;
}
if (!SDL_CreateWindowAndRenderer("examples/renderer/cliprect", WINDOW_WIDTH, WINDOW_HEIGHT, 0, &window, &renderer)) {
SDL_Log("Couldn't create window/renderer: %s", SDL_GetError());
return SDL_APP_FAILURE;
}
cliprect_direction.x = cliprect_direction.y = 1.0f;
last_time = SDL_GetTicks();
/* Textures are pixel data that we upload to the video hardware for fast drawing. Lots of 2D
engines refer to these as "sprites." We'll do a static texture (upload once, draw many
times) with data from a bitmap file. */
/* SDL_Surface is pixel data the CPU can access. SDL_Texture is pixel data the GPU can access.
Load a .bmp into a surface, move it to a texture from there. */
SDL_asprintf(&bmp_path, "%ssample.bmp", SDL_GetBasePath()); /* allocate a string of the full file path */
surface = SDL_LoadBMP(bmp_path);
if (!surface) {
SDL_Log("Couldn't load bitmap: %s", SDL_GetError());
return SDL_APP_FAILURE;
}
SDL_free(bmp_path); /* done with this, the file is loaded. */
texture = SDL_CreateTextureFromSurface(renderer, surface);
if (!texture) {
SDL_Log("Couldn't create static texture: %s", SDL_GetError());
return SDL_APP_FAILURE;
}
SDL_DestroySurface(surface); /* done with this, the texture has a copy of the pixels now. */
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)
{
const SDL_Rect cliprect = { (int) SDL_roundf(cliprect_position.x), (int) SDL_roundf(cliprect_position.y), CLIPRECT_SIZE, CLIPRECT_SIZE };
const Uint64 now = SDL_GetTicks();
const float elapsed = ((float) (now - last_time)) / 1000.0f; /* seconds since last iteration */
const float distance = elapsed * CLIPRECT_SPEED;
/* Set a new clipping rectangle position */
cliprect_position.x += distance * cliprect_direction.x;
if (cliprect_position.x < 0.0f) {
cliprect_position.x = 0.0f;
cliprect_direction.x = 1.0f;
} else if (cliprect_position.x >= (WINDOW_WIDTH - CLIPRECT_SIZE)) {
cliprect_position.x = (WINDOW_WIDTH - CLIPRECT_SIZE) - 1;
cliprect_direction.x = -1.0f;
}
cliprect_position.y += distance * cliprect_direction.y;
if (cliprect_position.y < 0.0f) {
cliprect_position.y = 0.0f;
cliprect_direction.y = 1.0f;
} else if (cliprect_position.y >= (WINDOW_HEIGHT - CLIPRECT_SIZE)) {
cliprect_position.y = (WINDOW_HEIGHT - CLIPRECT_SIZE) - 1;
cliprect_direction.y = -1.0f;
}
SDL_SetRenderClipRect(renderer, &cliprect);
last_time = now;
/* okay, now draw! */
/* Note that SDL_RenderClear is _not_ affected by the clipping rectangle! */
SDL_SetRenderDrawColor(renderer, 33, 33, 33, SDL_ALPHA_OPAQUE); /* grey, full alpha */
SDL_RenderClear(renderer); /* start with a blank canvas. */
/* stretch the texture across the entire window. Only the piece in the
clipping rectangle will actually render, though! */
SDL_RenderTexture(renderer, texture, NULL, NULL);
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_AppResult result)
{
SDL_DestroyTexture(texture);
/* SDL will clean up the window/renderer for us. */
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 254 KiB

Some files were not shown because too many files have changed in this diff Show more