├── .clang-format ├── .editorconfig ├── .gitattributes ├── .github └── workflows │ ├── documentation.yml │ ├── linux-ci.yml │ └── windows-ci.yml ├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── README.md ├── apply_format.sh ├── doxygen └── Doxyfile ├── examples ├── CMakeLists.txt ├── README.md ├── dll │ ├── README.md │ ├── dll.c │ └── main.cpp ├── general │ ├── main.cpp │ └── other_unit.cpp ├── opengl │ ├── README.md │ ├── glad │ │ ├── include │ │ │ ├── KHR │ │ │ │ └── khrplatform.h │ │ │ └── glad │ │ │ │ └── glad.h │ │ └── src │ │ │ └── glad.c │ └── main.cpp └── vulkan │ ├── README.md │ ├── main.cpp │ ├── simple_tri.frag.glsl │ ├── simple_tri.frag.spv │ ├── simple_tri.vert.glsl │ └── simple_tri.vert.spv └── sources └── cpp-sdl2 ├── color.hpp ├── event.hpp ├── exception.hpp ├── game_controller.hpp ├── haptic.hpp ├── joystick.hpp ├── mouse.hpp ├── pixel.hpp ├── rect.hpp ├── renderer.hpp ├── sdl.hpp ├── shared_object.hpp ├── simd.hpp ├── surface.hpp ├── texture.hpp ├── timer.hpp ├── utils.hpp ├── vec2.hpp └── window.hpp /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | 4 | AccessModifierOffset: -4 5 | AlignAfterOpenBracket: AlwaysBreak 6 | AlignConsecutiveAssignments: true 7 | AlignConsecutiveDeclarations: true 8 | AlignEscapedNewlines: Right 9 | AlignOperands: true 10 | AlignTrailingComments: true 11 | AllowAllParametersOfDeclarationOnNextLine: true 12 | AllowShortBlocksOnASingleLine: false 13 | AllowShortCaseLabelsOnASingleLine: true 14 | AllowShortFunctionsOnASingleLine: Inline 15 | AllowShortIfStatementsOnASingleLine: true 16 | AllowShortLoopsOnASingleLine: true 17 | AlwaysBreakAfterReturnType: None 18 | AlwaysBreakBeforeMultilineStrings: true 19 | AlwaysBreakTemplateDeclarations: Yes 20 | BinPackArguments: false 21 | BinPackParameters: false 22 | BraceWrapping: 23 | AfterClass: true 24 | AfterControlStatement: true 25 | AfterEnum: true 26 | AfterFunction: true 27 | AfterNamespace: true 28 | AfterStruct: true 29 | AfterUnion: true 30 | AfterExternBlock: false 31 | BeforeCatch: true 32 | BeforeElse: true 33 | IndentBraces: false 34 | SplitEmptyFunction: true 35 | SplitEmptyRecord: true 36 | SplitEmptyNamespace: true 37 | BreakBeforeBinaryOperators: NonAssignment 38 | BreakBeforeBraces: Custom 39 | BreakInheritanceList: BeforeComma 40 | BreakBeforeTernaryOperators: true 41 | BreakConstructorInitializers: BeforeComma 42 | BreakAfterJavaFieldAnnotations: false 43 | BreakStringLiterals: true 44 | ColumnLimit: 100 45 | CommentPragmas: '^/' 46 | CompactNamespaces: false 47 | ConstructorInitializerAllOnOneLineOrOnePerLine: true 48 | ConstructorInitializerIndentWidth: 4 49 | ContinuationIndentWidth: 4 50 | Cpp11BracedListStyle: true 51 | DerivePointerAlignment: false 52 | DisableFormat: false 53 | ExperimentalAutoDetectBinPacking: false 54 | FixNamespaceComments: true 55 | IncludeBlocks: Preserve 56 | IndentCaseLabels: false 57 | IndentPPDirectives: None 58 | IndentWidth: 4 59 | IndentWrappedFunctionNames: false 60 | KeepEmptyLinesAtTheStartOfBlocks: false 61 | MacroBlockBegin: '' 62 | MacroBlockEnd: '' 63 | MaxEmptyLinesToKeep: 1 64 | NamespaceIndentation: None 65 | PenaltyBreakAssignment: 2 66 | PenaltyBreakBeforeFirstCallParameter: 19 67 | PenaltyBreakComment: 300 68 | PenaltyBreakFirstLessLess: 120 69 | PenaltyBreakString: 1000 70 | PenaltyBreakTemplateDeclaration: 10 71 | PenaltyExcessCharacter: 1000000 72 | PenaltyReturnTypeOnItsOwnLine: 200 73 | PointerAlignment: Left 74 | ReflowComments: true 75 | SortIncludes: true 76 | SortUsingDeclarations: true 77 | SpaceAfterCStyleCast: false 78 | SpaceAfterTemplateKeyword: false 79 | SpaceBeforeAssignmentOperators: true 80 | SpaceBeforeCpp11BracedList: false 81 | SpaceBeforeCtorInitializerColon: true 82 | SpaceBeforeInheritanceColon: true 83 | SpaceBeforeParens: ControlStatements 84 | SpaceBeforeRangeBasedForLoopColon: true 85 | SpaceInEmptyParentheses: false 86 | SpacesBeforeTrailingComments: 1 87 | SpacesInAngles: false 88 | SpacesInContainerLiterals: false 89 | SpacesInCStyleCastParentheses: false 90 | SpacesInParentheses: false 91 | SpacesInSquareBrackets: false 92 | Standard: Cpp11 93 | TabWidth: 4 94 | UseTab: Always 95 | ... 96 | 97 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = tab 5 | tab_width = 4 6 | charset = utf-8 7 | end_of_line = lf 8 | insert_final_newline = true 9 | max_line_length = 80 10 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | examples/opengl/glad/* linguist-vendored 2 | -------------------------------------------------------------------------------- /.github/workflows/documentation.yml: -------------------------------------------------------------------------------- 1 | name: doc 2 | 3 | on: 4 | push: 5 | branches: [trunk] 6 | 7 | jobs: 8 | build-doc: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v2 12 | - name: get doxygen 13 | run: sudo apt-get install doxygen 14 | - name: build documentation 15 | run: | 16 | cd doxygen 17 | mkdir output 18 | doxygen Doxyfile 19 | cd .. 20 | - name: upload documentation 21 | uses: actions/upload-artifact@v2 22 | with: 23 | name: documentation 24 | path: doxygen/output/html 25 | - name: upload readme 26 | uses: actions/upload-artifact@v2 27 | with: 28 | name: readme 29 | path: README.md 30 | 31 | push-doc: 32 | runs-on: ubuntu-latest 33 | needs: build-doc 34 | steps: 35 | - uses: actions/checkout@v2 36 | with: 37 | ref: gh-pages 38 | fetch-depth: 0 39 | - name: reset to previous state 40 | run: | 41 | git config --local user.email "action@github.com" 42 | git config --local user.name "GitHub Action" 43 | git reset --hard HEAD~1 44 | rm -rf index.md doc 45 | - name: download documentation 46 | uses: actions/download-artifact@v2 47 | with: 48 | name: documentation 49 | path: doc 50 | - name: download readme 51 | uses: actions/download-artifact@v2 52 | with: 53 | name: readme 54 | - name: push documentation 55 | run: | 56 | mv README.md index.md 57 | git add index.md doc 58 | git commit -m ':robot: :gear: :black_nib: :green_book: :point_right: :octocat: :computer: :thumbsup:' 59 | git push --force origin gh-pages 60 | -------------------------------------------------------------------------------- /.github/workflows/linux-ci.yml: -------------------------------------------------------------------------------- 1 | name: linux 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | 9 | env: 10 | VULKAN_SDK_VER: 1.1.114.0 11 | VCPKG_VER: 2020.06 12 | 13 | steps: 14 | - uses: actions/checkout@v2 15 | 16 | - name: set environment 17 | run: | 18 | echo "::set-env name=VULKAN_SDK_URL:: https://sdk.lunarg.com/sdk/download/$VULKAN_SDK_VER/linux/vulkansdk-linux-x86_64-$VULKAN_SDK_VER.tar.gz" 19 | echo "::set-env name=VCPKG_PATH::${{github.workspace}}/vcpkg" 20 | echo "::set-env name=VULKAN_SDK::${{github.workspace}}/vulkan/$VULKAN_SDK_VER/x86_64" 21 | 22 | - name: install apt dependencies 23 | run: sudo apt-get install g++-8 libgl1-mesa-dev libgles2-mesa-dev 24 | 25 | - name: cache vulkan sdk 26 | id: cache-vulkan 27 | uses: actions/cache@v2 28 | with: 29 | path: ${{env.VULKAN_SDK}} 30 | key: vulkan-${{env.VULKAN_SDK_VER}} 31 | 32 | - name: install vulkan sdk 33 | if: steps.cache-vulkan.outputs.cache-hit != 'true' 34 | run: | 35 | mkdir vulkan 36 | cd vulkan 37 | wget -qO vulkansdk.tar.gz $VULKAN_SDK_URL 38 | tar xf vulkansdk.tar.gz 39 | cd ${{github.workspace}} 40 | 41 | - name: cache vcpkg 42 | id: cache-vcpkg 43 | uses: actions/cache@v2 44 | with: 45 | path: ${{env.VCPKG_PATH}} 46 | key: vcpkg-${{env.VCPKG_VER}}-vk-${{env.VULKAN_SDK_VER}} 47 | 48 | - name: install vcpkg 49 | if: steps.cache-vcpkg.outputs.cache-hit != 'true' 50 | run: | 51 | git clone https://github.com/Microsoft/vcpkg.git 52 | cd vcpkg 53 | git checkout --force $VCPKG_VER 54 | ./bootstrap-vcpkg.sh 55 | ./vcpkg install sdl2[*]:x64-linux 56 | ./vcpkg install opengl:x64-linux 57 | cd ${{github.workspace}} 58 | 59 | - name: configure cmake 60 | run: cmake -B build -DCMAKE_TOOLCHAIN_FILE=$VCPKG_PATH/scripts/buildsystems/vcpkg.cmake -DCPP_SDL2_BUILD_EXAMPLES=ON -DCPP_SDL2_ENABLE_OPENGL=ON -DCPP_SDL2_ENABLE_VULKAN=ON 61 | 62 | - name: build 63 | run: cmake --build build 64 | 65 | 66 | -------------------------------------------------------------------------------- /.github/workflows/windows-ci.yml: -------------------------------------------------------------------------------- 1 | name: windows 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | runs-on: windows-latest 8 | 9 | env: 10 | VULKAN_SDK_VER: 1.1.114.0 11 | VCPKG_VER: 2020.06 12 | 13 | steps: 14 | - uses: actions/checkout@v2 15 | 16 | - name: set environment 17 | run: | 18 | echo "::set-env name=VULKAN_SDK::C:\VulkanSDK\$env:VULKAN_SDK_VER" 19 | echo "::set-env name=VULKAN_SDK_URL::https://sdk.lunarg.com/sdk/download/$env:VULKAN_SDK_VER/windows/VulkanSDK-$env:VULKAN_SDK_VER-Installer.exe" 20 | echo "::set-env name=VCPKG_PATH::${{github.workspace}}/vcpkg" 21 | 22 | # TODO: for some reason, this always fail, taking time for nothing 23 | # - name: cache vulkan sdk 24 | # id: cache-vulkan 25 | # uses: actions/cache@v2 26 | # with: 27 | # path: ${{env.VULKAN_SDK}} 28 | # key: vulkan-${{env.VULKAN_SDK_VER}} 29 | 30 | - name: install vulkan sdk 31 | # if: steps.cache-vulkan.outputs.cache-hit != 'true' 32 | run: | 33 | Invoke-WebRequest "${env:VULKAN_SDK_URL}?Human=true" -OutFile VulkanSDK.exe -v 34 | echo "Installing SDK ..." 35 | Start-Process .\VulkanSDK.exe /S -Wait 36 | 37 | - name: cache dependencies 38 | id: cache-vcpkg 39 | uses: actions/cache@v2 40 | with: 41 | path: ${{env.VCPKG_PATH}} 42 | key: vcpkg-${{env.VCPKG_VER}}-vk-${{env.VULKAN_SDK_VER}} 43 | 44 | - name: install dependencies 45 | if: steps.cache-vcpkg.outputs.cache-hit != 'true' 46 | run: | 47 | git clone https://github.com/Microsoft/vcpkg.git 48 | cd vcpkg 49 | git checkout --force $env:VULKAN_VER 50 | .\bootstrap-vcpkg.bat 51 | .\vcpkg.exe install sdl2[*]:x64-windows 52 | cd ${{github.workspace}} 53 | 54 | - name: configure cmake 55 | run: cmake -B build -G "Visual Studio 16 2019" -A x64 -DCMAKE_TOOLCHAIN_FILE="$env:VCPKG_PATH/scripts/buildsystems/vcpkg.cmake" -DCPP_SDL2_BUILD_EXAMPLES=ON -DCPP_SDL2_ENABLE_OPENGL=ON -DCPP_SDL2_ENABLE_VULKAN=ON 56 | 57 | - name: build 58 | run: cmake --build build 59 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /doxygen/output/ 2 | build 3 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.12) 2 | 3 | project(cpp_sdl2 4 | VERSION 0.1.0 5 | DESCRIPTION "Basic C++17 bindings to SDL2" 6 | HOMEPAGE_URL "https://github.com/Edhebi/cpp-sdl2" 7 | LANGUAGES C CXX 8 | ) 9 | 10 | set(CPP_SDL2_SOURCES 11 | ${CMAKE_CURRENT_SOURCE_DIR}/sources/cpp-sdl2/color.hpp 12 | ${CMAKE_CURRENT_SOURCE_DIR}/sources/cpp-sdl2/event.hpp 13 | ${CMAKE_CURRENT_SOURCE_DIR}/sources/cpp-sdl2/exception.hpp 14 | ${CMAKE_CURRENT_SOURCE_DIR}/sources/cpp-sdl2/game_controller.hpp 15 | ${CMAKE_CURRENT_SOURCE_DIR}/sources/cpp-sdl2/haptic.hpp 16 | ${CMAKE_CURRENT_SOURCE_DIR}/sources/cpp-sdl2/joystick.hpp 17 | ${CMAKE_CURRENT_SOURCE_DIR}/sources/cpp-sdl2/mouse.hpp 18 | ${CMAKE_CURRENT_SOURCE_DIR}/sources/cpp-sdl2/pixel.hpp 19 | ${CMAKE_CURRENT_SOURCE_DIR}/sources/cpp-sdl2/rect.hpp 20 | ${CMAKE_CURRENT_SOURCE_DIR}/sources/cpp-sdl2/renderer.hpp 21 | ${CMAKE_CURRENT_SOURCE_DIR}/sources/cpp-sdl2/sdl.hpp 22 | ${CMAKE_CURRENT_SOURCE_DIR}/sources/cpp-sdl2/shared_object.hpp 23 | ${CMAKE_CURRENT_SOURCE_DIR}/sources/cpp-sdl2/simd.hpp 24 | ${CMAKE_CURRENT_SOURCE_DIR}/sources/cpp-sdl2/surface.hpp 25 | ${CMAKE_CURRENT_SOURCE_DIR}/sources/cpp-sdl2/texture.hpp 26 | ${CMAKE_CURRENT_SOURCE_DIR}/sources/cpp-sdl2/timer.hpp 27 | ${CMAKE_CURRENT_SOURCE_DIR}/sources/cpp-sdl2/utils.hpp 28 | ${CMAKE_CURRENT_SOURCE_DIR}/sources/cpp-sdl2/vec2.hpp 29 | ${CMAKE_CURRENT_SOURCE_DIR}/sources/cpp-sdl2/window.hpp 30 | ) 31 | 32 | add_library(cpp_sdl2 INTERFACE) 33 | target_compile_features(cpp_sdl2 INTERFACE cxx_std_17) 34 | target_sources(cpp_sdl2 INTERFACE ${CPP_SDL2_SOURCES}) 35 | target_include_directories(cpp_sdl2 INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/sources) 36 | 37 | find_package(SDL2 CONFIG REQUIRED) 38 | target_link_libraries(cpp_sdl2 INTERFACE SDL2::SDL2) 39 | 40 | option(CPP_SDL2_ENABLE_OPENGL "Enable opengl functionalities for windows." OFF) 41 | if(CPP_SDL2_ENABLE_OPENGL) 42 | find_package(OpenGL REQUIRED) 43 | target_link_libraries(cpp_sdl2 INTERFACE OpenGL::GL) 44 | target_compile_definitions(cpp_sdl2 INTERFACE CPP_SDL2_ENABLE_OPENGL) 45 | endif() 46 | 47 | option(CPP_SDL2_ENABLE_VULKAN "Enable vulkan functionalities for windows." OFF) 48 | if(CPP_SDL2_ENABLE_VULKAN) 49 | find_package(Vulkan REQUIRED) 50 | target_link_libraries(cpp_sdl2 INTERFACE Vulkan::Vulkan) 51 | target_compile_definitions(cpp_sdl2 INTERFACE CPP_SDL2_ENABLE_VULKAN) 52 | endif() 53 | 54 | option(CPP_SDL2_ENABLE_SDL_IMAGE "Use SDL_Image to provide image loading tools" OFF) 55 | if(CPP_SDL2_ENABLE_SDL_IMAGE) 56 | if(NOT TARGET SDL2::SDL2_image) 57 | find_package(sdl2-image CONFIG REQUIRED) 58 | endif() 59 | target_link_libraries(cpp_sdl2 INTERFACE SDL2::SDL2_image) 60 | target_compile_definitions(cpp_sdl2 INTERFACE CPP_SDL2_ENABLE_SDL_IMAGE) 61 | endif() 62 | 63 | option(CPP_SDL2_DISABLE_EXCEPTIONS "Disable exceptions for cpp-sdl2" OFF) 64 | if(CPP_SDL2_DISABLE_EXCEPTIONS) 65 | target_compile_definitions(cpp_sdl2 INTERFACE CPP_SDL2_DISABLE_EXCEPTIONS) 66 | endif() 67 | 68 | # TODO: handle installation 69 | 70 | option(CPP_SDL2_BUILD_EXAMPLES "Build examples" OFF) 71 | if(CPP_SDL2_BUILD_EXAMPLES) 72 | add_subdirectory(examples) 73 | endif() 74 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018-2020 Edhebi, Ybalrid and contributors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # cpp-sdl2 2 | [![linux](https://github.com/Edhebi/cpp-sdl2/workflows/linux/badge.svg)][linux-badge] 3 | [![windows](https://github.com/Edhebi/cpp-sdl2/workflows/windows/badge.svg)][windows-badge] 4 | [![doc](https://github.com/Edhebi/cpp-sdl2/workflows/doc/badge.svg)][doc-badge] 5 | 6 | [linux-badge]: https://github.com/Edhebi/cpp-sdl2/actions?query=workflow%3Alinux 7 | [windows-badge]: https://github.com/Edhebi/cpp-sdl2/actions?query=workflow%3Awindows 8 | [doc-badge]: https://edhebi.github.io/cpp-sdl2/doc/ 9 | 10 | Basic C++17 bindings to [SDL2], implemented as an header-only library 11 | 12 | [SDL2]: https://wiki.libsdl.org/FrontPage 13 | 14 | ## Documentation 15 | 16 | This project uses doxygen for its documentation, you can find it [here](https://edhebi.github.io/cpp-sdl2/doc). 17 | 18 | ## Usage 19 | 20 | This library has been written in C++17. It should work out of the box with any modern compiler, but you may need to set 21 | some flags ([gcc][gcc-c++17], [clang][clang-c++17], [msvc][msvc-c++17]). If you use the cmake target, this will be done 22 | automatically. 23 | 24 | [gcc-c++17]: https://gcc.gnu.org/projects/cxx-status.html#cxx17 25 | [clang-c++17]: https://clang.llvm.org/cxx_status.html#cxx17 26 | [msvc-c++17]: https://docs.microsoft.com/en-us/cpp/build/reference/std-specify-language-standard-version 27 | 28 | This library is header-only, meaning that there is no build step. You only need to make the `sources` directory visible 29 | to your compiler, and replace your `SDL.h` include by `#include 84 | #include 85 | 86 | #include // Using C-style rand 87 | #include 88 | 89 | int main([[maybe_unused]] int argc, [[maybe_unused]] char* argv[]) 90 | { 91 | std::srand(static_cast(std::time(nullptr))); 92 | 93 | // The following objects manages the lifetime of SDL resources RAII style 94 | 95 | sdl::Root root{SDL_INIT_EVENTS}; 96 | sdl::Window window{"Random Colors", {600, 600}}; 97 | 98 | sdl::Renderer renderer = window.make_renderer(); 99 | 100 | sdl::Color background = sdl::Color::Black(); 101 | 102 | bool done = false; 103 | bool redraw = true; 104 | 105 | while (!done) 106 | { 107 | if (redraw) 108 | { 109 | renderer.clear(background); 110 | renderer.present(); 111 | redraw = false; 112 | } 113 | 114 | sdl::Event event; 115 | while (event.pull()) 116 | { 117 | if (event.type == SDL_QUIT) done = true; 118 | 119 | if (event.type == SDL_MOUSEBUTTONUP) 120 | { 121 | color.r = std::rand() % 256; 122 | color.g = std::rand() % 256; 123 | color.b = std::rand() % 256; 124 | redraw = true; 125 | } 126 | } 127 | } 128 | 129 | // Cleanup is done automatically 130 | return 0; 131 | } 132 | ``` 133 | -------------------------------------------------------------------------------- /apply_format.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | find . -type f -name "*.hpp" -exec clang-format -i {} + 4 | -------------------------------------------------------------------------------- /examples/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(cpp_sdl2_example_general general/main.cpp general/other_unit.cpp) 2 | target_link_libraries(cpp_sdl2_example_general PRIVATE cpp_sdl2 SDL2::SDL2main) 3 | 4 | add_library(cpp_sdl2_example_dll_shared SHARED dll/dll.c) 5 | target_link_libraries(cpp_sdl2_example_dll_shared PRIVATE cpp_sdl2) 6 | add_executable(cpp_sdl2_example_dll dll/main.cpp) 7 | target_link_libraries(cpp_sdl2_example_dll PRIVATE cpp_sdl2_example_dll_shared cpp_sdl2 SDL2::SDL2main) 8 | 9 | if(CPP_SDL2_ENABLE_OPENGL) 10 | add_executable(cpp_sdl2_example_opengl opengl/main.cpp opengl/glad/src/glad.c) 11 | target_include_directories(cpp_sdl2_example_opengl PRIVATE opengl/glad/include) 12 | target_link_libraries(cpp_sdl2_example_opengl PRIVATE cpp_sdl2 SDL2::SDL2main) 13 | endif() 14 | 15 | if(CPP_SDL2_ENABLE_VULKAN) 16 | add_executable(cpp_sdl2_example_vulkan vulkan/main.cpp) 17 | target_sources(cpp_sdl2_example_vulkan PRIVATE 18 | vulkan/simple_tri.frag.glsl 19 | vulkan/simple_tri.frag.spv 20 | vulkan/simple_tri.vert.glsl 21 | vulkan/simple_tri.vert.spv 22 | ) 23 | target_link_libraries(cpp_sdl2_example_vulkan PRIVATE cpp_sdl2 SDL2::SDL2main) 24 | endif() 25 | -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | # cpp-sdl2 examples 2 | 3 | This folder contains a series of short programs that demonstrate this library for two purposes : 4 | 5 | - Being able to test that the wrappers are working properly 6 | - Show some canonical use cases of theses wrapers 7 | 8 | ## Folder content 9 | 10 | - **general** : A program that calls a number of functionalities from the API 11 | - **dll** : A dynamic library called "my_dll", and a program that loads it and call functions through cpp-sdl2. 0% platform specific code here 12 | - **gl** : A program that display one triangle on a dark blue background. Used to demonstrate how to initialize painlessly a GL window anc context with cpp-sdl2 13 | - **vk** : A program that display one triangle on a dark blue background. Used to demonstate how to initialze painlessly a Vulkan Window, Instance and a (platform specific) Surface object with cpp-sdl2 14 | 15 | The **cmake-modules** direcory contains cmake scripts used to find dependencies for these progarms, notably an _arguably better_ than the default one to find SDL2 that has been tested on multiple OSes. 16 | 17 | 18 | The top-level diretory is one project that contains all the mentioned examples 19 | -------------------------------------------------------------------------------- /examples/dll/README.md: -------------------------------------------------------------------------------- 1 | # cpp-sdl2 dll 2 | 3 | This example shows the interface to load dynamic libraries and functions from them. -------------------------------------------------------------------------------- /examples/dll/dll.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #ifdef _MSC_VER 4 | // On visual studio, we need to use this "decoration" on our function 5 | #define EXPORT __declspec(dllexport) 6 | #else 7 | // On other platform, we just define this symbol so that the code will compile 8 | #define EXPORT 9 | #endif 10 | 11 | void EXPORT print_hello() 12 | { 13 | printf("Hello DLL!\n"); 14 | } 15 | 16 | int EXPORT get_the_answer_to_life_the_universe_and_everything() 17 | { 18 | return 42; 19 | } 20 | -------------------------------------------------------------------------------- /examples/dll/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | // define pointer types 6 | using dll_function_ptr = void (*)(); 7 | using dll_get_int_ptr = int (*)(); 8 | 9 | int main(int argc, char* argv[]) 10 | { 11 | // Here's how you load a dynamiy library 12 | auto so_instance = sdl::SharedObject("cpp_sdl2_example_dll_shared.dll"); 13 | 14 | // Load function pointer 15 | const dll_function_ptr dll_function = 16 | (dll_function_ptr)so_instance.function_pointer( // note the pointer cast 17 | "print_hello"); 18 | 19 | // You can specify the function signature as the function pointer type in 20 | // template argument. Avoid ugly casts like the one above 21 | auto get_the_answer_to_life_the_universe_and_everything = 22 | so_instance.function_pointer( 23 | "get_the_answer_to_life_the_universe_and_everything"); 24 | 25 | // Call the functions 26 | dll_function(); 27 | std::cout << "answer is : " 28 | << get_the_answer_to_life_the_universe_and_everything() << "\n"; 29 | 30 | // The following show how to catch errors gracefully: 31 | // Exception are thrown if library or procedure cannot be found 32 | std::unique_ptr dll_not_here; 33 | dll_function_ptr print_nuclear_code = nullptr; 34 | try 35 | { 36 | dll_not_here = std::make_unique("magical_library"); 37 | } 38 | catch (const std::exception& e) 39 | { 40 | std::cerr << "Cannot load DLL " << e.what() << "\n"; 41 | } 42 | 43 | try 44 | { 45 | print_nuclear_code = 46 | (dll_function_ptr)so_instance.function_pointer("print_secret"); 47 | } 48 | catch (const std::exception& e) 49 | { 50 | std::cerr << "Failed to locate function in library " << e.what() 51 | << "\n"; 52 | } 53 | 54 | return 0; 55 | } 56 | -------------------------------------------------------------------------------- /examples/general/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include // Using C-style rand 4 | #include 5 | #include 6 | 7 | // check linkage to other unit 8 | 9 | void useless_function(); 10 | 11 | int main(int argc, char* argv[]) 12 | { 13 | using namespace std::literals::chrono_literals; 14 | 15 | (void)argc; 16 | (void)argv; 17 | 18 | std::srand(unsigned(std::time(nullptr))); 19 | 20 | // The following classes manages the lifetime of SDL declared resources RAII 21 | // style 22 | 23 | auto root = 24 | sdl::Root(SDL_INIT_EVENTS | SDL_INIT_GAMECONTROLLER | SDL_INIT_HAPTIC); 25 | 26 | auto window = sdl::Window{"Random Colors", {600, 600}}; 27 | 28 | auto renderer = window.make_renderer(); 29 | 30 | auto color = sdl::Color::Black(); 31 | 32 | auto done = false; 33 | auto redraw = true; 34 | auto event = sdl::Event{}; 35 | 36 | // maximum power rumble for 200 millisecond 37 | sdl::Haptic::Effect hard_rumble_effect; 38 | hard_rumble_effect.type = SDL_HAPTIC_LEFTRIGHT; 39 | hard_rumble_effect.leftright.length = 200; 40 | hard_rumble_effect.leftright.large_magnitude = 0xFFFF; 41 | hard_rumble_effect.leftright.small_magnitude = 0xFFFF; 42 | 43 | // This utility function will open all the game controllers connected to the 44 | // system that are known from the SDL GameController API 45 | auto controllers = sdl::GameController::open_all_available_controllers(); 46 | 47 | // Try to get the first controller's haptic device 48 | auto main_haptic = !controllers.empty() ? controllers.front().open_haptic() 49 | : sdl::Haptic(); 50 | 51 | // This is to store the effect 52 | sdl::Haptic::InstalledEffect hard_rumble; 53 | 54 | try 55 | { 56 | if (main_haptic.valid() 57 | && main_haptic.is_effect_compatible(hard_rumble_effect)) 58 | { 59 | hard_rumble = main_haptic.new_effect(hard_rumble_effect); 60 | hard_rumble.run(1); 61 | } 62 | } 63 | catch (sdl::Exception const& e) 64 | { 65 | sdl::show_message_box( 66 | SDL_MESSAGEBOX_ERROR, 67 | "Couldn't install or play SDL haptic effect on controller", 68 | e.what(), 69 | window); 70 | } 71 | 72 | while (!done) 73 | { 74 | if (redraw) 75 | { 76 | renderer.clear(color); 77 | renderer.present(); 78 | redraw = false; 79 | } 80 | 81 | event.wait(); 82 | 83 | switch (event.type) 84 | { 85 | #if SDL_VERSION_ATLEAST(2, 0, 9) 86 | case SDL_CONTROLLERAXISMOTION: 87 | sdl::GameController::non_owning(event.caxis.which) 88 | .rumble( 89 | event.caxis.axis == SDL_CONTROLLER_AXIS_LEFTX 90 | || event.caxis.axis == SDL_CONTROLLER_AXIS_LEFTY 91 | ? 0xFFFF 92 | : 0x0, 93 | event.caxis.axis == SDL_CONTROLLER_AXIS_RIGHTX 94 | || event.caxis.axis == SDL_CONTROLLER_AXIS_RIGHTY 95 | ? 0xFFFF 96 | : 0x0, 97 | 10); 98 | 99 | break; 100 | #endif 101 | case SDL_QUIT: done = true; break; 102 | case SDL_MOUSEBUTTONUP: 103 | case SDL_KEYUP: 104 | if (event.type == SDL_KEYUP // kludge 105 | && event.key.keysym.scancode == SDL_SCANCODE_Q) 106 | useless_function(); 107 | case SDL_CONTROLLERBUTTONDOWN: 108 | color.r = std::rand() % 256; 109 | color.g = std::rand() % 256; 110 | color.b = std::rand() % 256; 111 | redraw = true; 112 | break; 113 | case SDL_CONTROLLERBUTTONUP: 114 | if (event.cbutton.button == SDL_CONTROLLER_BUTTON_A) 115 | { 116 | main_haptic.run_effect(hard_rumble); 117 | } 118 | default: break; 119 | } 120 | } 121 | return 0; 122 | 123 | // Cleanup is done automatically 124 | } 125 | -------------------------------------------------------------------------------- /examples/general/other_unit.cpp: -------------------------------------------------------------------------------- 1 | #include "cpp-sdl2/sdl.hpp" 2 | 3 | void useless_function() 4 | { 5 | sdl::show_message_box( 6 | SDL_MESSAGEBOX_INFORMATION, 7 | "Testing...", 8 | "The only existance of this file is to test if we can have 2 " 9 | "compilation unit including the whole library and not explode. This " 10 | "has been an issue before..."); 11 | } 12 | -------------------------------------------------------------------------------- /examples/opengl/README.md: -------------------------------------------------------------------------------- 1 | # cpp-sdl2 OpenGL example 2 | 3 | This is a small "hello opengl" program, but that uses cpp-sdl2 for windowing and context management. 4 | 5 | the interesting code for our demo amont to this : 6 | 7 | Include like this (with an OpenGL loader of your choice) 8 | ```cpp 9 | // To use the GL wrapper, define the following 10 | #define CPP_SDL2_GL_WINDOW 11 | #include 12 | #include 13 | ``` 14 | 15 | //Begining of `main()` 16 | ```cpp 17 | 18 | // Create an SDL window, with the SDL_WINDOW_OPENGL flags 19 | auto window = sdl::Window("OpenGL", {800, 600}, SDL_WINDOW_OPENGL); 20 | 21 | // Bevore creating a context, set the flag for the version you want to get, 22 | // here we want Core OpenGL 3.3 23 | sdl::Window::gl_set_attribute( 24 | SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); 25 | sdl::Window::gl_set_attribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); 26 | sdl::Window::gl_set_attribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); 27 | 28 | // Create your context 29 | auto context = window.create_context(); 30 | 31 | // You are done, now you can use OpenGL! 32 | 33 | // Call whatever function loader you want, in this example we use GLAD 34 | // because we generated a really small version of it for 3.3 Core: 35 | gladLoadGL(); 36 | 37 | ``` 38 | -------------------------------------------------------------------------------- /examples/opengl/glad/include/KHR/khrplatform.h: -------------------------------------------------------------------------------- 1 | #ifndef __khrplatform_h_ 2 | #define __khrplatform_h_ 3 | 4 | /* 5 | ** Copyright (c) 2008-2018 The Khronos Group Inc. 6 | ** 7 | ** Permission is hereby granted, free of charge, to any person obtaining a 8 | ** copy of this software and/or associated documentation files (the 9 | ** "Materials"), to deal in the Materials without restriction, including 10 | ** without limitation the rights to use, copy, modify, merge, publish, 11 | ** distribute, sublicense, and/or sell copies of the Materials, and to 12 | ** permit persons to whom the Materials are furnished to do so, subject to 13 | ** the following conditions: 14 | ** 15 | ** The above copyright notice and this permission notice shall be included 16 | ** in all copies or substantial portions of the Materials. 17 | ** 18 | ** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | ** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | ** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | ** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | ** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 23 | ** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 24 | ** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. 25 | */ 26 | 27 | /* Khronos platform-specific types and definitions. 28 | * 29 | * The master copy of khrplatform.h is maintained in the Khronos EGL 30 | * Registry repository at https://github.com/KhronosGroup/EGL-Registry 31 | * The last semantic modification to khrplatform.h was at commit ID: 32 | * 67a3e0864c2d75ea5287b9f3d2eb74a745936692 33 | * 34 | * Adopters may modify this file to suit their platform. Adopters are 35 | * encouraged to submit platform specific modifications to the Khronos 36 | * group so that they can be included in future versions of this file. 37 | * Please submit changes by filing pull requests or issues on 38 | * the EGL Registry repository linked above. 39 | * 40 | * 41 | * See the Implementer's Guidelines for information about where this file 42 | * should be located on your system and for more details of its use: 43 | * http://www.khronos.org/registry/implementers_guide.pdf 44 | * 45 | * This file should be included as 46 | * #include 47 | * by Khronos client API header files that use its types and defines. 48 | * 49 | * The types in khrplatform.h should only be used to define API-specific types. 50 | * 51 | * Types defined in khrplatform.h: 52 | * khronos_int8_t signed 8 bit 53 | * khronos_uint8_t unsigned 8 bit 54 | * khronos_int16_t signed 16 bit 55 | * khronos_uint16_t unsigned 16 bit 56 | * khronos_int32_t signed 32 bit 57 | * khronos_uint32_t unsigned 32 bit 58 | * khronos_int64_t signed 64 bit 59 | * khronos_uint64_t unsigned 64 bit 60 | * khronos_intptr_t signed same number of bits as a pointer 61 | * khronos_uintptr_t unsigned same number of bits as a pointer 62 | * khronos_ssize_t signed size 63 | * khronos_usize_t unsigned size 64 | * khronos_float_t signed 32 bit floating point 65 | * khronos_time_ns_t unsigned 64 bit time in nanoseconds 66 | * khronos_utime_nanoseconds_t unsigned time interval or absolute time in 67 | * nanoseconds 68 | * khronos_stime_nanoseconds_t signed time interval in nanoseconds 69 | * khronos_boolean_enum_t enumerated boolean type. This should 70 | * only be used as a base type when a client API's boolean type is 71 | * an enum. Client APIs which use an integer or other type for 72 | * booleans cannot use this as the base type for their boolean. 73 | * 74 | * Tokens defined in khrplatform.h: 75 | * 76 | * KHRONOS_FALSE, KHRONOS_TRUE Enumerated boolean false/true values. 77 | * 78 | * KHRONOS_SUPPORT_INT64 is 1 if 64 bit integers are supported; otherwise 0. 79 | * KHRONOS_SUPPORT_FLOAT is 1 if floats are supported; otherwise 0. 80 | * 81 | * Calling convention macros defined in this file: 82 | * KHRONOS_APICALL 83 | * KHRONOS_APIENTRY 84 | * KHRONOS_APIATTRIBUTES 85 | * 86 | * These may be used in function prototypes as: 87 | * 88 | * KHRONOS_APICALL void KHRONOS_APIENTRY funcname( 89 | * int arg1, 90 | * int arg2) KHRONOS_APIATTRIBUTES; 91 | */ 92 | 93 | #if defined(__SCITECH_SNAP__) && !defined(KHRONOS_STATIC) 94 | # define KHRONOS_STATIC 1 95 | #endif 96 | 97 | /*------------------------------------------------------------------------- 98 | * Definition of KHRONOS_APICALL 99 | *------------------------------------------------------------------------- 100 | * This precedes the return type of the function in the function prototype. 101 | */ 102 | #if defined(KHRONOS_STATIC) 103 | /* If the preprocessor constant KHRONOS_STATIC is defined, make the 104 | * header compatible with static linking. */ 105 | # define KHRONOS_APICALL 106 | #elif defined(_WIN32) 107 | # define KHRONOS_APICALL __declspec(dllimport) 108 | #elif defined (__SYMBIAN32__) 109 | # define KHRONOS_APICALL IMPORT_C 110 | #elif defined(__ANDROID__) 111 | # define KHRONOS_APICALL __attribute__((visibility("default"))) 112 | #else 113 | # define KHRONOS_APICALL 114 | #endif 115 | 116 | /*------------------------------------------------------------------------- 117 | * Definition of KHRONOS_APIENTRY 118 | *------------------------------------------------------------------------- 119 | * This follows the return type of the function and precedes the function 120 | * name in the function prototype. 121 | */ 122 | #if defined(_WIN32) && !defined(_WIN32_WCE) && !defined(KHRONOS_STATIC) 123 | /* Win32 but not WinCE */ 124 | # define KHRONOS_APIENTRY __stdcall 125 | #else 126 | # define KHRONOS_APIENTRY 127 | #endif 128 | 129 | /*------------------------------------------------------------------------- 130 | * Definition of KHRONOS_APIATTRIBUTES 131 | *------------------------------------------------------------------------- 132 | * This follows the closing parenthesis of the function prototype arguments. 133 | */ 134 | #if defined (__ARMCC_2__) 135 | #define KHRONOS_APIATTRIBUTES __softfp 136 | #else 137 | #define KHRONOS_APIATTRIBUTES 138 | #endif 139 | 140 | /*------------------------------------------------------------------------- 141 | * basic type definitions 142 | *-----------------------------------------------------------------------*/ 143 | #if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__GNUC__) || defined(__SCO__) || defined(__USLC__) 144 | 145 | 146 | /* 147 | * Using 148 | */ 149 | #include 150 | typedef int32_t khronos_int32_t; 151 | typedef uint32_t khronos_uint32_t; 152 | typedef int64_t khronos_int64_t; 153 | typedef uint64_t khronos_uint64_t; 154 | #define KHRONOS_SUPPORT_INT64 1 155 | #define KHRONOS_SUPPORT_FLOAT 1 156 | 157 | #elif defined(__VMS ) || defined(__sgi) 158 | 159 | /* 160 | * Using 161 | */ 162 | #include 163 | typedef int32_t khronos_int32_t; 164 | typedef uint32_t khronos_uint32_t; 165 | typedef int64_t khronos_int64_t; 166 | typedef uint64_t khronos_uint64_t; 167 | #define KHRONOS_SUPPORT_INT64 1 168 | #define KHRONOS_SUPPORT_FLOAT 1 169 | 170 | #elif defined(_WIN32) && !defined(__SCITECH_SNAP__) 171 | 172 | /* 173 | * Win32 174 | */ 175 | typedef __int32 khronos_int32_t; 176 | typedef unsigned __int32 khronos_uint32_t; 177 | typedef __int64 khronos_int64_t; 178 | typedef unsigned __int64 khronos_uint64_t; 179 | #define KHRONOS_SUPPORT_INT64 1 180 | #define KHRONOS_SUPPORT_FLOAT 1 181 | 182 | #elif defined(__sun__) || defined(__digital__) 183 | 184 | /* 185 | * Sun or Digital 186 | */ 187 | typedef int khronos_int32_t; 188 | typedef unsigned int khronos_uint32_t; 189 | #if defined(__arch64__) || defined(_LP64) 190 | typedef long int khronos_int64_t; 191 | typedef unsigned long int khronos_uint64_t; 192 | #else 193 | typedef long long int khronos_int64_t; 194 | typedef unsigned long long int khronos_uint64_t; 195 | #endif /* __arch64__ */ 196 | #define KHRONOS_SUPPORT_INT64 1 197 | #define KHRONOS_SUPPORT_FLOAT 1 198 | 199 | #elif 0 200 | 201 | /* 202 | * Hypothetical platform with no float or int64 support 203 | */ 204 | typedef int khronos_int32_t; 205 | typedef unsigned int khronos_uint32_t; 206 | #define KHRONOS_SUPPORT_INT64 0 207 | #define KHRONOS_SUPPORT_FLOAT 0 208 | 209 | #else 210 | 211 | /* 212 | * Generic fallback 213 | */ 214 | #include 215 | typedef int32_t khronos_int32_t; 216 | typedef uint32_t khronos_uint32_t; 217 | typedef int64_t khronos_int64_t; 218 | typedef uint64_t khronos_uint64_t; 219 | #define KHRONOS_SUPPORT_INT64 1 220 | #define KHRONOS_SUPPORT_FLOAT 1 221 | 222 | #endif 223 | 224 | 225 | /* 226 | * Types that are (so far) the same on all platforms 227 | */ 228 | typedef signed char khronos_int8_t; 229 | typedef unsigned char khronos_uint8_t; 230 | typedef signed short int khronos_int16_t; 231 | typedef unsigned short int khronos_uint16_t; 232 | 233 | /* 234 | * Types that differ between LLP64 and LP64 architectures - in LLP64, 235 | * pointers are 64 bits, but 'long' is still 32 bits. Win64 appears 236 | * to be the only LLP64 architecture in current use. 237 | */ 238 | #ifdef _WIN64 239 | typedef signed long long int khronos_intptr_t; 240 | typedef unsigned long long int khronos_uintptr_t; 241 | typedef signed long long int khronos_ssize_t; 242 | typedef unsigned long long int khronos_usize_t; 243 | #else 244 | typedef signed long int khronos_intptr_t; 245 | typedef unsigned long int khronos_uintptr_t; 246 | typedef signed long int khronos_ssize_t; 247 | typedef unsigned long int khronos_usize_t; 248 | #endif 249 | 250 | #if KHRONOS_SUPPORT_FLOAT 251 | /* 252 | * Float type 253 | */ 254 | typedef float khronos_float_t; 255 | #endif 256 | 257 | #if KHRONOS_SUPPORT_INT64 258 | /* Time types 259 | * 260 | * These types can be used to represent a time interval in nanoseconds or 261 | * an absolute Unadjusted System Time. Unadjusted System Time is the number 262 | * of nanoseconds since some arbitrary system event (e.g. since the last 263 | * time the system booted). The Unadjusted System Time is an unsigned 264 | * 64 bit value that wraps back to 0 every 584 years. Time intervals 265 | * may be either signed or unsigned. 266 | */ 267 | typedef khronos_uint64_t khronos_utime_nanoseconds_t; 268 | typedef khronos_int64_t khronos_stime_nanoseconds_t; 269 | #endif 270 | 271 | /* 272 | * Dummy value used to pad enum types to 32 bits. 273 | */ 274 | #ifndef KHRONOS_MAX_ENUM 275 | #define KHRONOS_MAX_ENUM 0x7FFFFFFF 276 | #endif 277 | 278 | /* 279 | * Enumerated boolean type 280 | * 281 | * Values other than zero should be considered to be true. Therefore 282 | * comparisons should not be made against KHRONOS_TRUE. 283 | */ 284 | typedef enum { 285 | KHRONOS_FALSE = 0, 286 | KHRONOS_TRUE = 1, 287 | KHRONOS_BOOLEAN_ENUM_FORCE_SIZE = KHRONOS_MAX_ENUM 288 | } khronos_boolean_enum_t; 289 | 290 | #endif /* __khrplatform_h_ */ 291 | -------------------------------------------------------------------------------- /examples/opengl/main.cpp: -------------------------------------------------------------------------------- 1 | // To use the GL wrapper, define the following 2 | #include 3 | #include 4 | 5 | // The following arrays are the geometry data for one triangle with vertex 6 | // colors 7 | 8 | // clang-format off 9 | GLfloat tri_vertex_buffer[] 10 | { /*X Y Z R G B*/ 11 | 0, .5, 0, 1, 0, 0, //Vertex 2 12 | .5, -.5, 0, 0, 1, 0, //Vertex 1 13 | -.5, -.5, 0, 0, 0, 1, //Vertex 0 14 | }; 15 | // clang-format on 16 | 17 | GLuint tri_index_buffer[]{0, 1, 2}; 18 | 19 | // The following strings are a shader program that just bland the above data to 20 | // the screen 21 | const GLchar vert_shader_source[] = R"_( 22 | #version 330 core 23 | 24 | layout (location = 0) in vec3 in_pos; 25 | layout (location = 1) in vec3 in_rgb; 26 | 27 | out vec4 color_output; 28 | 29 | void main() 30 | { 31 | gl_Position = vec4(in_pos, 1); 32 | color_output = vec4(in_rgb, 1); 33 | })_"; 34 | 35 | const GLchar frag_shader_source[] = R"_( 36 | #version 330 core 37 | 38 | in vec4 color_output; 39 | out vec4 frag_color; 40 | 41 | void main() 42 | { 43 | frag_color = color_output; 44 | } 45 | )_"; 46 | 47 | // This function print context version information to a console 48 | void pirnt_gl_version(); 49 | 50 | // This function build a shader program from shader source code 51 | GLuint build_shader_program( 52 | const GLchar* vert_source, const GLchar* frag_source); 53 | 54 | int main(int argc, char* argv[]) 55 | { 56 | (void)argc, (void)argv; 57 | // Create an SDL window, with the SDL_WINDOW_OPENGL flags 58 | auto window = sdl::Window("OpenGL", {800, 600}, SDL_WINDOW_OPENGL); 59 | 60 | // Bevore creating a context, set the flag for the version you want to get, 61 | // here we want Core OpenGL 3.3 62 | sdl::Window::gl_set_attribute( 63 | SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); 64 | sdl::Window::gl_set_attribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); 65 | sdl::Window::gl_set_attribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); 66 | 67 | // Create your context 68 | auto context = window.create_context(); 69 | 70 | // You are done, now you can use OpenGL! 71 | 72 | // Call whatever function loader you want, in this example we use GLAD 73 | // because we generated a really small version of it for 3.3 Core: 74 | gladLoadGL(); 75 | 76 | pirnt_gl_version(); 77 | GLuint shader_program = 78 | build_shader_program(vert_shader_source, frag_shader_source); 79 | 80 | // We build a VertexArrayObject to reference our triangle geometry in the 81 | // GPU 82 | // 83 | // Our geometry data is expressed as one VertexBufferObject and one 84 | // ElementBufferObject. 85 | // 86 | // The VertexBufferObject contains 6 floats per vertex. 3 of them is a 3D 87 | // vector for position, the 3 other are an RGB color value in linear 88 | // colorspace. 89 | // 90 | // Our shader want position data in location 0 and color data in location 1 91 | GLuint tri_vao, tri_vbo, tri_ebo; 92 | glGenVertexArrays(1, &tri_vao); 93 | glGenBuffers(1, &tri_vbo); 94 | glGenBuffers(1, &tri_ebo); 95 | 96 | glBindVertexArray(tri_vao); 97 | 98 | // Build VBO 99 | glBindBuffer(GL_ARRAY_BUFFER, tri_vbo); 100 | glBufferData( 101 | GL_ARRAY_BUFFER, 102 | 18 * sizeof(GLfloat), 103 | tri_vertex_buffer, 104 | GL_STATIC_DRAW); 105 | glVertexAttribPointer( 106 | 0, 107 | 3, 108 | GL_FLOAT, 109 | GL_FALSE, 110 | 6 * sizeof(GLfloat), 111 | (void*)(0 * sizeof(GLfloat))); 112 | glEnableVertexAttribArray(0); 113 | glVertexAttribPointer( 114 | 1, 115 | 3, 116 | GL_FLOAT, 117 | GL_FALSE, 118 | 6 * sizeof(GLfloat), 119 | (void*)(3 * sizeof(GLfloat))); 120 | glEnableVertexAttribArray(1); 121 | 122 | // Build EBO 123 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, tri_ebo); 124 | glBufferData( 125 | GL_ELEMENT_ARRAY_BUFFER, 126 | 3 * sizeof(tri_index_buffer), 127 | tri_index_buffer, 128 | GL_STATIC_DRAW); 129 | 130 | // Clean behind yourself 131 | glBindVertexArray(0); 132 | 133 | // Render loop 134 | bool running = true; 135 | while (running) 136 | { 137 | sdl::Event e; 138 | while (e.poll()) 139 | { 140 | if (e.type == SDL_QUIT) running = false; 141 | } 142 | 143 | glClearColor(.1f, .2f, .3f, 1); 144 | glClear(GL_COLOR_BUFFER_BIT); 145 | 146 | glUseProgram(shader_program); 147 | glBindVertexArray(tri_vao); 148 | glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_INT, nullptr); 149 | 150 | window.gl_swap(); 151 | } 152 | 153 | glUseProgram(0); 154 | glDeleteProgram(shader_program); 155 | glBindVertexArray(0); 156 | glDeleteVertexArrays(1, &tri_vao); 157 | glDeleteBuffers(1, &tri_vbo); 158 | glDeleteBuffers(1, &tri_ebo); 159 | 160 | return 0; 161 | } 162 | 163 | void pirnt_gl_version() 164 | { 165 | printf("%s\n", glGetString(GL_VENDOR)); 166 | printf("%s\n", glGetString(GL_RENDERER)); 167 | int major, minor; 168 | glGetIntegerv(GL_MAJOR_VERSION, &major); 169 | glGetIntegerv(GL_MINOR_VERSION, &minor); 170 | printf("context version: %d.%d\n", major, minor); 171 | } 172 | 173 | GLuint build_shader_program( 174 | const GLchar* vert_source, const GLchar* frag_source) 175 | { 176 | GLuint vert_shader = glCreateShader(GL_VERTEX_SHADER); 177 | GLuint frag_shader = glCreateShader(GL_FRAGMENT_SHADER); 178 | GLuint program = glCreateProgram(); 179 | 180 | glShaderSource(vert_shader, 1, &vert_source, nullptr); 181 | glShaderSource(frag_shader, 1, &frag_source, nullptr); 182 | glCompileShader(vert_shader); 183 | glCompileShader(frag_shader); 184 | 185 | GLint success = 0; 186 | GLchar info_log[512]; 187 | 188 | if (glGetShaderiv(vert_shader, GL_COMPILE_STATUS, &success); !success) 189 | { 190 | glGetShaderInfoLog(vert_shader, sizeof info_log, nullptr, info_log); 191 | fprintf(stderr, "%s\n", info_log); 192 | abort(); 193 | } 194 | if (glGetShaderiv(frag_shader, GL_COMPILE_STATUS, &success); !success) 195 | { 196 | glGetShaderInfoLog(frag_shader, sizeof info_log, nullptr, info_log); 197 | fprintf(stderr, "%s\n", info_log); 198 | abort(); 199 | } 200 | 201 | glAttachShader(program, vert_shader); 202 | glAttachShader(program, frag_shader); 203 | glLinkProgram(program); 204 | if (glGetProgramiv(program, GL_LINK_STATUS, &success); !success) 205 | { 206 | glGetProgramInfoLog(program, sizeof info_log, nullptr, info_log); 207 | fprintf(stderr, "%s\n", info_log); 208 | abort(); 209 | } 210 | 211 | glDeleteShader(vert_shader); 212 | glDeleteShader(frag_shader); 213 | return program; 214 | } 215 | -------------------------------------------------------------------------------- /examples/vulkan/README.md: -------------------------------------------------------------------------------- 1 | # cpp-sdl2 Vulkan example 2 | 3 | This is a small "hello vulkan" program, but that uses cpp-sdl2 for enumerating device extensions and to create the platform specific vkSurface object 4 | 5 | the interesting code for our demo amont to this : 6 | 7 | Include like this (with an OpenGL loader of your choice) 8 | ```cpp 9 | #define NOMINMAX 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | 17 | // Define the flollowing token 18 | #define CPP_SDL2_VK_WINDOW 19 | #include 20 | 21 | // use validation layers only on debug builds 22 | #if defined(_DEBUG) || defined(DEBUG) 23 | #define VK_APP_USE_LAYER true 24 | #endif 25 | 26 | #ifndef VK_APP_USE_LAYER 27 | #define VK_APP_USE_LAYER false 28 | #endif 29 | ``` 30 | 31 | Note that we also manage here a flag to know if we should activate validation layers or not 32 | 33 | 34 | 35 | //Begining of `main()` 36 | ```cpp 37 | uint32_t width = 800; 38 | uint32_t height = 600; 39 | 40 | // Create widnow with the SDL_WINDOW_VULKAN configuration flag 41 | sdl::Window window("Vulkan", {int(width), int(height)}, SDL_WINDOW_VULKAN); 42 | auto vk_instance_exts = window.vk_get_instance_extensions(); 43 | auto vk_layers = 44 | std::vector{"VK_LAYER_LUNARG_standard_validation"}; 45 | 46 | const vk::ApplicationInfo vk_app_info{"Vulkan", 47 | VK_MAKE_VERSION(1, 0, 0), 48 | "not_an_engine", 49 | VK_MAKE_VERSION(1, 0, 0)}; 50 | 51 | if (VK_APP_USE_LAYER) vk_instance_exts.push_back("VK_EXT_debug_utils"); 52 | 53 | auto vk_instance = vk::createInstanceUnique(vk::InstanceCreateInfo{ 54 | {}, 55 | &vk_app_info, 56 | VK_APP_USE_LAYER ? uint32_t(vk_layers.size()) : 0, 57 | VK_APP_USE_LAYER ? vk_layers.data() : nullptr, 58 | uint32_t(vk_instance_exts.size()), 59 | vk_instance_exts.data()}); 60 | 61 | #if VK_APP_USE_LAYER 62 | auto vk_messenger = vk_instance->createDebugUtilsMessengerEXTUnique( 63 | vk::DebugUtilsMessengerCreateInfoEXT{ 64 | {}, 65 | vk::DebugUtilsMessageSeverityFlagBitsEXT::eError 66 | | vk::DebugUtilsMessageSeverityFlagBitsEXT::eWarning 67 | | vk::DebugUtilsMessageSeverityFlagBitsEXT::eVerbose 68 | | vk::DebugUtilsMessageSeverityFlagBitsEXT::eInfo, 69 | vk::DebugUtilsMessageTypeFlagBitsEXT::eGeneral 70 | | vk::DebugUtilsMessageTypeFlagBitsEXT::eValidation 71 | | vk::DebugUtilsMessageTypeFlagBitsEXT::ePerformance, 72 | vk_debug_callback // pointer to our own function here 73 | }, 74 | nullptr, 75 | vk::DispatchLoaderDynamic{vk_instance.get()}); 76 | #endif 77 | 78 | // Create a surface 79 | auto surface = window.vk_create_unique_surface(vk_instance.get()); 80 | ``` 81 | -------------------------------------------------------------------------------- /examples/vulkan/main.cpp: -------------------------------------------------------------------------------- 1 | #define NOMINMAX 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #define VULKAN_HPP_TYPESAFE_CONVERSION 1 8 | #include 9 | 10 | #include 11 | 12 | // use validation layers only on debug builds 13 | #if 0 && defined(_DEBUG) || defined(DEBUG) 14 | #define VK_APP_USE_LAYER true 15 | #endif 16 | 17 | #ifndef VK_APP_USE_LAYER 18 | #define VK_APP_USE_LAYER false 19 | #endif 20 | 21 | static std::array clear_color{.1f, .2f, .3f, 1.f}; 22 | 23 | static VKAPI_ATTR VkBool32 VKAPI_CALL vk_debug_callback( 24 | VkDebugUtilsMessageSeverityFlagBitsEXT severity, 25 | VkDebugUtilsMessageTypeFlagsEXT message_type, 26 | const VkDebugUtilsMessengerCallbackDataEXT* callback_data, 27 | void* user_data) 28 | { 29 | (void)message_type, (void)severity, (void)user_data; 30 | std::cerr << "validation layer: " << callback_data->pMessage << std::endl; 31 | return VK_FALSE; 32 | } 33 | 34 | std::vector read_file(const std::string& filename) 35 | { 36 | std::ifstream file(filename, std::ios::ate | std::ios::binary); 37 | if (!file.is_open()) 38 | throw std::runtime_error("failed to open file " + filename); 39 | 40 | size_t filesize = (size_t)file.tellg(); 41 | std::vector buffer(filesize); 42 | 43 | file.seekg(0); 44 | file.read(buffer.data(), filesize); 45 | 46 | return buffer; 47 | } 48 | 49 | int main(int argc, char* argv[]) 50 | { 51 | (void)argc; 52 | (void)argv; 53 | 54 | uint32_t width = 800; 55 | uint32_t height = 600; 56 | 57 | // Create widnow with the SDL_WINDOW_VULKAN configuration flag 58 | sdl::Window window("Vulkan", {int(width), int(height)}, SDL_WINDOW_VULKAN); 59 | auto vk_instance_exts = window.vk_get_instance_extensions(); 60 | auto vk_layers = 61 | std::vector{"VK_LAYER_LUNARG_standard_validation"}; 62 | 63 | const vk::ApplicationInfo vk_app_info{"Vulkan", 64 | VK_MAKE_VERSION(1, 0, 0), 65 | "not_an_engine", 66 | VK_MAKE_VERSION(1, 0, 0)}; 67 | 68 | if (VK_APP_USE_LAYER) vk_instance_exts.push_back("VK_EXT_debug_utils"); 69 | 70 | auto vk_instance = vk::createInstanceUnique(vk::InstanceCreateInfo{ 71 | {}, 72 | &vk_app_info, 73 | VK_APP_USE_LAYER ? uint32_t(vk_layers.size()) : 0, 74 | VK_APP_USE_LAYER ? vk_layers.data() : nullptr, 75 | uint32_t(vk_instance_exts.size()), 76 | vk_instance_exts.data()}); 77 | 78 | #if VK_APP_USE_LAYER 79 | auto vk_messenger = vk_instance->createDebugUtilsMessengerEXTUnique( 80 | vk::DebugUtilsMessengerCreateInfoEXT{ 81 | {}, 82 | vk::DebugUtilsMessageSeverityFlagBitsEXT::eError 83 | | vk::DebugUtilsMessageSeverityFlagBitsEXT::eWarning 84 | | vk::DebugUtilsMessageSeverityFlagBitsEXT::eVerbose 85 | | vk::DebugUtilsMessageSeverityFlagBitsEXT::eInfo, 86 | vk::DebugUtilsMessageTypeFlagBitsEXT::eGeneral 87 | | vk::DebugUtilsMessageTypeFlagBitsEXT::eValidation 88 | | vk::DebugUtilsMessageTypeFlagBitsEXT::ePerformance, 89 | vk_debug_callback // pointer to our own function here 90 | }, 91 | nullptr, 92 | vk::DispatchLoaderDynamic{vk_instance.get()}); 93 | #endif 94 | 95 | // Create a surface 96 | auto surface = window.vk_create_unique_surface( 97 | static_cast(vk_instance.get())); 98 | 99 | // enumerate devices 100 | auto vk_physical_devices = vk_instance->enumeratePhysicalDevices(); 101 | auto vk_physical_device = vk_physical_devices.back(); 102 | auto queue_family_properties = 103 | vk_physical_device.getQueueFamilyProperties(); 104 | 105 | auto graphicsQueueFamilyIndex = static_cast(std::distance( 106 | queue_family_properties.begin(), 107 | std::find_if( 108 | queue_family_properties.begin(), 109 | queue_family_properties.end(), 110 | [](vk::QueueFamilyProperties const& qfp) { 111 | return qfp.queueFlags & vk::QueueFlagBits::eGraphics; 112 | }))); 113 | 114 | uint32_t presentQueueFamilyIndex = 0u; 115 | for (uint32_t i = 0; i < queue_family_properties.size(); ++i) 116 | { 117 | if (vk_physical_device.getSurfaceSupportKHR(i, surface.get())) 118 | { 119 | presentQueueFamilyIndex = i; 120 | } 121 | } 122 | std::set uniqueQueueFamilyIndices = {graphicsQueueFamilyIndex, 123 | presentQueueFamilyIndex}; 124 | 125 | std::vector FamilyIndices = {uniqueQueueFamilyIndices.begin(), 126 | uniqueQueueFamilyIndices.end()}; 127 | 128 | std::vector queueCreateInfos; 129 | 130 | float queuePriority = 0.0f; 131 | for (auto queueFamilyIndex : uniqueQueueFamilyIndices) 132 | { 133 | queueCreateInfos.push_back( 134 | vk::DeviceQueueCreateInfo{vk::DeviceQueueCreateFlags(), 135 | static_cast(queueFamilyIndex), 136 | 1, 137 | &queuePriority}); 138 | } 139 | 140 | const std::vector deviceExtensions = { 141 | VK_KHR_SWAPCHAIN_EXTENSION_NAME}; 142 | 143 | vk::UniqueDevice device = 144 | vk_physical_device.createDeviceUnique(vk::DeviceCreateInfo( 145 | vk::DeviceCreateFlags(), 146 | static_cast(queueCreateInfos.size()), 147 | queueCreateInfos.data(), 148 | 0, 149 | nullptr, 150 | static_cast(deviceExtensions.size()), 151 | deviceExtensions.data())); 152 | 153 | uint32_t imageCount = 2; 154 | 155 | struct SM 156 | { 157 | vk::SharingMode sharingMode; 158 | uint32_t familyIndicesCount; 159 | uint32_t* familyIndicesDataPtr; 160 | } sharingModeUtil{ 161 | (graphicsQueueFamilyIndex != presentQueueFamilyIndex) 162 | ? SM{vk::SharingMode::eConcurrent, 2u, FamilyIndices.data()} 163 | : SM{vk::SharingMode::eExclusive, 164 | 0u, 165 | static_cast(nullptr)}}; 166 | 167 | // needed for validation warnings 168 | auto capabilities = vk_physical_device.getSurfaceCapabilitiesKHR(*surface); 169 | auto formats = vk_physical_device.getSurfaceFormatsKHR(*surface); 170 | 171 | auto format = vk::Format::eB8G8R8A8Unorm; 172 | auto extent = vk::Extent2D{width, height}; 173 | 174 | vk::SwapchainCreateInfoKHR swapChainCreateInfo( 175 | {}, 176 | surface.get(), 177 | imageCount, 178 | format, 179 | vk::ColorSpaceKHR::eSrgbNonlinear, 180 | extent, 181 | 1, 182 | vk::ImageUsageFlagBits::eColorAttachment, 183 | sharingModeUtil.sharingMode, 184 | sharingModeUtil.familyIndicesCount, 185 | sharingModeUtil.familyIndicesDataPtr, 186 | vk::SurfaceTransformFlagBitsKHR::eIdentity, 187 | vk::CompositeAlphaFlagBitsKHR::eOpaque, 188 | vk::PresentModeKHR::eFifo, 189 | true, 190 | nullptr); 191 | 192 | auto swapChain = device->createSwapchainKHRUnique(swapChainCreateInfo); 193 | 194 | std::vector swapChainImages = 195 | device->getSwapchainImagesKHR(swapChain.get()); 196 | 197 | std::vector imageViews; 198 | imageViews.reserve(swapChainImages.size()); 199 | for (auto image : swapChainImages) 200 | { 201 | vk::ImageViewCreateInfo imageViewCreateInfo( 202 | vk::ImageViewCreateFlags(), 203 | image, 204 | vk::ImageViewType::e2D, 205 | format, 206 | vk::ComponentMapping{vk::ComponentSwizzle::eR, 207 | vk::ComponentSwizzle::eG, 208 | vk::ComponentSwizzle::eB, 209 | vk::ComponentSwizzle::eA}, 210 | vk::ImageSubresourceRange{ 211 | vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1}); 212 | imageViews.push_back( 213 | device->createImageViewUnique(imageViewCreateInfo)); 214 | } 215 | 216 | const auto fragment_data = read_file("../vk/simple_tri.frag.spv"); 217 | const auto vertex_data = read_file("../vk/simple_tri.vert.spv"); 218 | 219 | vk::ShaderModuleCreateInfo fragment_shader_module_create_info( 220 | {}, fragment_data.size(), (uint32_t*)fragment_data.data()), 221 | vertex_shader_module_create_info( 222 | {}, vertex_data.size(), (uint32_t*)vertex_data.data()); 223 | 224 | auto frag_shader_module = 225 | device->createShaderModuleUnique(fragment_shader_module_create_info); 226 | auto vert_shader_module = 227 | device->createShaderModuleUnique(vertex_shader_module_create_info); 228 | 229 | auto vertShaderStageInfo = vk::PipelineShaderStageCreateInfo{ 230 | {}, vk::ShaderStageFlagBits::eVertex, *vert_shader_module, "main"}; 231 | 232 | auto fragShaderStageInfo = vk::PipelineShaderStageCreateInfo{ 233 | {}, vk::ShaderStageFlagBits::eFragment, *frag_shader_module, "main"}; 234 | 235 | auto pipelineShaderStages = std::vector{ 236 | vertShaderStageInfo, fragShaderStageInfo}; 237 | 238 | auto vertexInputInfo = 239 | vk::PipelineVertexInputStateCreateInfo{{}, 0u, nullptr, 0u, nullptr}; 240 | 241 | auto inputAssembly = vk::PipelineInputAssemblyStateCreateInfo{ 242 | {}, vk::PrimitiveTopology::eTriangleList, false}; 243 | 244 | auto viewport = vk::Viewport{0.0f, 245 | 0.0f, 246 | static_cast(width), 247 | static_cast(height), 248 | 0.0f, 249 | 1.0f}; 250 | 251 | auto scissor = vk::Rect2D{{0, 0}, extent}; 252 | 253 | auto viewportState = 254 | vk::PipelineViewportStateCreateInfo{{}, 1, &viewport, 1, &scissor}; 255 | 256 | auto rasterizer = vk::PipelineRasterizationStateCreateInfo{ 257 | {}, 258 | /*depthClamp*/ false, 259 | /*rasterizeDiscard*/ false, 260 | vk::PolygonMode::eFill, 261 | {}, 262 | /*frontFace*/ vk::FrontFace::eCounterClockwise, 263 | {}, 264 | {}, 265 | {}, 266 | {}, 267 | 1.0f}; 268 | 269 | auto multisampling = vk::PipelineMultisampleStateCreateInfo{ 270 | {}, vk::SampleCountFlagBits::e1, false, 1.0}; 271 | 272 | auto colorBlendAttachment = vk::PipelineColorBlendAttachmentState{ 273 | {}, 274 | /*srcCol*/ vk::BlendFactor::eOne, 275 | /*dstCol*/ vk::BlendFactor::eZero, 276 | /*colBlend*/ vk::BlendOp::eAdd, 277 | /*srcAlpha*/ vk::BlendFactor::eOne, 278 | /*dstAlpha*/ vk::BlendFactor::eZero, 279 | /*alphaBlend*/ vk::BlendOp::eAdd, 280 | vk::ColorComponentFlagBits::eR | vk::ColorComponentFlagBits::eG 281 | | vk::ColorComponentFlagBits::eB | vk::ColorComponentFlagBits::eA}; 282 | 283 | auto colorBlending = vk::PipelineColorBlendStateCreateInfo{ 284 | {}, 285 | /*logicOpEnable=*/false, 286 | vk::LogicOp::eCopy, 287 | /*attachmentCount=*/1, 288 | /*colourAttachments=*/&colorBlendAttachment}; 289 | 290 | auto pipelineLayout = device->createPipelineLayoutUnique({}, nullptr); 291 | 292 | auto colorAttachment = 293 | vk::AttachmentDescription{{}, 294 | format, 295 | vk::SampleCountFlagBits::e1, 296 | vk::AttachmentLoadOp::eClear, 297 | vk::AttachmentStoreOp::eStore, 298 | {}, 299 | {}, 300 | {}, 301 | vk::ImageLayout::ePresentSrcKHR}; 302 | 303 | auto colourAttachmentRef = 304 | vk::AttachmentReference{0, vk::ImageLayout::eColorAttachmentOptimal}; 305 | 306 | auto subpass = vk::SubpassDescription{{}, 307 | vk::PipelineBindPoint::eGraphics, 308 | /*inAttachmentCount*/ 0, 309 | nullptr, 310 | 1, 311 | &colourAttachmentRef}; 312 | 313 | auto semaphoreCreateInfo = vk::SemaphoreCreateInfo{}; 314 | auto imageAvailableSemaphore = 315 | device->createSemaphoreUnique(semaphoreCreateInfo); 316 | auto renderFinishedSemaphore = 317 | device->createSemaphoreUnique(semaphoreCreateInfo); 318 | 319 | auto subpassDependency = 320 | vk::SubpassDependency{VK_SUBPASS_EXTERNAL, 321 | 0, 322 | vk::PipelineStageFlagBits::eColorAttachmentOutput, 323 | vk::PipelineStageFlagBits::eColorAttachmentOutput, 324 | {}, 325 | vk::AccessFlagBits::eColorAttachmentRead 326 | | vk::AccessFlagBits::eColorAttachmentWrite}; 327 | 328 | auto renderPass = device->createRenderPassUnique(vk::RenderPassCreateInfo{ 329 | {}, 1, &colorAttachment, 1, &subpass, 1, &subpassDependency}); 330 | 331 | auto pipelineCreateInfo = 332 | vk::GraphicsPipelineCreateInfo{{}, 333 | 2, 334 | pipelineShaderStages.data(), 335 | &vertexInputInfo, 336 | &inputAssembly, 337 | nullptr, 338 | &viewportState, 339 | &rasterizer, 340 | &multisampling, 341 | nullptr, 342 | &colorBlending, 343 | nullptr, 344 | *pipelineLayout, 345 | *renderPass, 346 | 0}; 347 | 348 | auto pipeline = 349 | device->createGraphicsPipelineUnique({}, pipelineCreateInfo); 350 | 351 | auto framebuffers = std::vector(imageCount); 352 | for (size_t i = 0; i < imageViews.size(); i++) 353 | { 354 | framebuffers[i] = device->createFramebufferUnique( 355 | vk::FramebufferCreateInfo{{}, 356 | *renderPass, 357 | 1, 358 | &(*imageViews[i]), 359 | extent.width, 360 | extent.height, 361 | 1}); 362 | } 363 | auto commandPoolUnique = device->createCommandPoolUnique( 364 | {{}, graphicsQueueFamilyIndex}); 365 | 366 | std::vector commandBuffers = 367 | device->allocateCommandBuffersUnique(vk::CommandBufferAllocateInfo( 368 | commandPoolUnique.get(), 369 | vk::CommandBufferLevel::ePrimary, 370 | std::uint32_t(framebuffers.size()))); 371 | 372 | auto deviceQueue = device->getQueue(graphicsQueueFamilyIndex, 0); 373 | auto presentQueue = device->getQueue(presentQueueFamilyIndex, 0); 374 | 375 | for (size_t i = 0; i < commandBuffers.size(); i++) 376 | { 377 | auto beginInfo = vk::CommandBufferBeginInfo{}; 378 | commandBuffers[i]->begin(beginInfo); 379 | 380 | vk::ClearValue clearValues{clear_color}; 381 | 382 | auto renderPassBeginInfo = 383 | vk::RenderPassBeginInfo{renderPass.get(), 384 | framebuffers[i].get(), 385 | vk::Rect2D{{0, 0}, extent}, 386 | 1, 387 | &clearValues}; 388 | 389 | commandBuffers[i]->beginRenderPass( 390 | renderPassBeginInfo, vk::SubpassContents::eInline); 391 | commandBuffers[i]->bindPipeline( 392 | vk::PipelineBindPoint::eGraphics, pipeline.get()); 393 | commandBuffers[i]->draw(3, 1, 0, 0); 394 | commandBuffers[i]->endRenderPass(); 395 | commandBuffers[i]->end(); 396 | } 397 | 398 | bool running = true; 399 | while (running) 400 | { 401 | sdl::Event e; 402 | while (e.poll()) 403 | { 404 | if (e.type == SDL_QUIT) running = false; 405 | } 406 | 407 | auto imageIndex = device->acquireNextImageKHR( 408 | swapChain.get(), 409 | std::numeric_limits::max(), 410 | imageAvailableSemaphore.get(), 411 | {}); 412 | 413 | vk::PipelineStageFlags waitStageMask = 414 | vk::PipelineStageFlagBits::eColorAttachmentOutput; 415 | 416 | auto submitInfo = 417 | vk::SubmitInfo{1, 418 | &imageAvailableSemaphore.get(), 419 | &waitStageMask, 420 | 1, 421 | &commandBuffers[imageIndex.value].get(), 422 | 1, 423 | &renderFinishedSemaphore.get()}; 424 | 425 | deviceQueue.submit(submitInfo, {}); 426 | 427 | auto presentInfo = vk::PresentInfoKHR{1, 428 | &renderFinishedSemaphore.get(), 429 | 1, 430 | &swapChain.get(), 431 | &imageIndex.value}; 432 | 433 | presentQueue.presentKHR(presentInfo); 434 | device->waitIdle(); 435 | } 436 | 437 | return 0; 438 | } 439 | -------------------------------------------------------------------------------- /examples/vulkan/simple_tri.frag.glsl: -------------------------------------------------------------------------------- 1 | #version 450 2 | #extension GL_ARB_separate_shader_objects : enable 3 | 4 | layout(location = 0) in vec3 fragColor; 5 | layout(location = 0) out vec4 outColor; 6 | void main() { 7 | outColor = vec4(fragColor, 1.0); 8 | } 9 | -------------------------------------------------------------------------------- /examples/vulkan/simple_tri.frag.spv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edhebi/cpp-sdl2/8dbc2c9189cbd61ff0aa95e03f130f56318dd87b/examples/vulkan/simple_tri.frag.spv -------------------------------------------------------------------------------- /examples/vulkan/simple_tri.vert.glsl: -------------------------------------------------------------------------------- 1 | #version 450 2 | #extension GL_ARB_separate_shader_objects : enable 3 | 4 | out gl_PerVertex { 5 | vec4 gl_Position; 6 | }; 7 | layout(location = 0) out vec3 fragColor; 8 | vec2 positions[3] = vec2[]( 9 | vec2(0, -0.5), 10 | vec2(0.5, 0.5), 11 | vec2(-0.5, 0.5) 12 | ); 13 | vec3 colors[3] = vec3[]( 14 | vec3(1.0, 0.0, 0.0), 15 | vec3(0.0, 1.0, 0.0), 16 | vec3(0.0, 0.0, 1.0) 17 | ); 18 | void main() { 19 | gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0); 20 | fragColor = colors[gl_VertexIndex]; 21 | } 22 | -------------------------------------------------------------------------------- /examples/vulkan/simple_tri.vert.spv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edhebi/cpp-sdl2/8dbc2c9189cbd61ff0aa95e03f130f56318dd87b/examples/vulkan/simple_tri.vert.spv -------------------------------------------------------------------------------- /sources/cpp-sdl2/color.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "exception.hpp" 4 | #include 5 | #include 6 | 7 | namespace sdl 8 | { 9 | ///C++ wrapping around the SDL_Color structure 10 | class Color : public SDL_Color 11 | { 12 | public: 13 | ///Construct an sdl::Color object. Color will be black by default 14 | constexpr Color() : SDL_Color{0, 0, 0, 255} {} 15 | 16 | ///Construct an sdl Color::object with the given 8 bit color values. Alpha channel to fully opaque by default 17 | /// \param r 8 bit quantity of red 18 | /// \param g 8 bit quantity of green 19 | /// \param b 8 bit quantity of blue 20 | /// \param a 8 bit opacity level. Default to 255 (0xff) 21 | constexpr Color(Uint8 r, Uint8 g, Uint8 b, Uint8 a = 255) 22 | : SDL_Color{r, g, b, a} 23 | { 24 | } 25 | 26 | ///Default copy ctor 27 | constexpr Color(Color const&) = default; 28 | 29 | ///Default move ctor 30 | constexpr Color(Color&&) = default; 31 | 32 | ///Converting constructor that will initialize the color with a given 32 bit value, according to the provided PixelFormat 33 | Color(Uint32 raw, SDL_PixelFormat const& format) 34 | { 35 | SDL_GetRGBA(raw, &format, &r, &g, &b, &a); 36 | } 37 | 38 | /// \copydoc Color(Uint32 raw, SDL_PixelFormat const& format) 39 | Color(Uint32 raw, Uint32 format) 40 | { 41 | auto f = SDL_AllocFormat(format); 42 | if (!f) throw Exception{"SDL_AllocFormat"}; 43 | SDL_GetRGBA(raw, f, &r, &g, &b, &a); 44 | SDL_FreeFormat(f); 45 | } 46 | 47 | ///Default copy assing operator 48 | Color& operator=(Color const&) = default; 49 | 50 | ///Default move assing operator 51 | Color& operator=(Color&&) = default; 52 | 53 | ///Return the color of the current object as a 32bit number (4 bytes) 54 | /// \param format pixel format to use 55 | Uint32 as_uint(SDL_PixelFormat const& format) const 56 | { 57 | if (SDL_ISPIXELFORMAT_ALPHA(format.format)) 58 | { 59 | return SDL_MapRGBA(&format, r, g, b, a); 60 | } 61 | else 62 | { 63 | return SDL_MapRGB(&format, r, g, b); 64 | } 65 | } 66 | 67 | /// \copydoc as_uint(SDL_PixelFormat const& format) const 68 | Uint32 as_uint(Uint32 format) const 69 | { 70 | auto f = SDL_AllocFormat(format); 71 | if (!f) throw Exception{"SDL_AllocFormat"}; 72 | auto raw = as_uint(*f); 73 | SDL_FreeFormat(f); 74 | return raw; 75 | } 76 | 77 | /// Return true if both colors are identical 78 | bool operator==(Color const& c) const 79 | { 80 | return r == c.r && g == c.g && b == c.b && a == c.a; 81 | } 82 | 83 | /// Output stream overload that will print the value of the current color as a 4D vector of 8bit numbers 84 | friend std::ostream& operator<<(std::ostream& stream, Color const& c) 85 | { 86 | return stream << "(r:" << c.r << ",g:" << c.g << ",b:" << c.b 87 | << ",a:" << c.a << ")"; 88 | } 89 | 90 | static constexpr Color White() { return {255, 255, 255, 255}; } 91 | static constexpr Color Black() { return {0, 0, 0, 255}; } 92 | static constexpr Color Red() { return {255, 0, 0, 255}; } 93 | static constexpr Color Green() { return {0, 255, 0, 255}; } 94 | static constexpr Color Blue() { return {0, 0, 255, 255}; } 95 | static constexpr Color Transparent() { return {0, 0, 0, 0}; } 96 | }; 97 | 98 | } // namespace sdl 99 | -------------------------------------------------------------------------------- /sources/cpp-sdl2/event.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "exception.hpp" 4 | 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #include // use SDL2 packing 13 | 14 | // Visual Studio 2019 has became TOO pedantic about member 15 | // initialization. Supress IntelliSence CppCoreGuideline 16 | // "MEMBER_UNINIT" warning. The static ananlyser doesn't like our union 17 | // initialized only by writing over the whole object with the value of an 18 | // SDL_Event. Have to agree with it on the fact that this is using dark magic 19 | // **BUT** we actually **KNOW** what we are doing here, thank you very much 20 | #if _MSC_VER >= 1910 21 | #pragma warning(push) 22 | #pragma warning(disable : 26495) 23 | #endif 24 | 25 | namespace sdl 26 | { 27 | ///\brief Object that represent an event captured by SDL 28 | /// 29 | ///This union has the exact same memory layout as the SDL_Event structure. 30 | ///An SDL_Event and an sdl::Event object are "the same bits" in memory if they hold infos about the same event. 31 | ///cpp-sdl2 convert the raw SDL_Event into an sdl::Event object to add an object-oriented API around them 32 | union Event 33 | { 34 | // this is copy-pasted from the definition of SDL_Event in 35 | // 36 | public: 37 | Uint32 type; ///< Event type, shared with all events 38 | SDL_CommonEvent common; ///< Common event data 39 | SDL_WindowEvent window; ///< Window event data 40 | SDL_KeyboardEvent key; ///< Keyboard event data 41 | SDL_TextEditingEvent edit; ///< Text editing event data 42 | SDL_TextInputEvent text; ///< Text input event data 43 | SDL_MouseMotionEvent motion; ///< Mouse motion event data 44 | SDL_MouseButtonEvent button; ///< Mouse button event data 45 | SDL_MouseWheelEvent wheel; ///< Mouse wheel event data 46 | SDL_JoyAxisEvent jaxis; ///< Joystick axis event data 47 | SDL_JoyBallEvent jball; ///< Joystick ball event data 48 | SDL_JoyHatEvent jhat; ///< Joystick hat event data 49 | SDL_JoyButtonEvent jbutton; ///< Joystick button event data 50 | SDL_JoyDeviceEvent jdevice; ///< Joystick device change event data 51 | SDL_ControllerAxisEvent caxis; ///< Game Controller axis event data 52 | SDL_ControllerButtonEvent cbutton; ///< Game Controller button event data 53 | SDL_ControllerDeviceEvent cdevice; ///< Game Controller device event data 54 | SDL_AudioDeviceEvent adevice; ///< Audio device event data 55 | SDL_QuitEvent quit; ///< Quit request event data 56 | SDL_UserEvent user; ///< Custom event data 57 | SDL_SysWMEvent syswm; ///< System dependent window event data 58 | SDL_TouchFingerEvent tfinger; ///< Touch finger event data 59 | SDL_MultiGestureEvent mgesture; ///< Gesture event data 60 | SDL_DollarGestureEvent dgesture; ///< Gesture event data 61 | SDL_DropEvent drop; ///< Drag and drop event data 62 | #if SDL_VERSION_ATLEAST(2, 0, 9) 63 | SDL_SensorEvent sensor; /// Sensor event data 64 | SDL_DisplayEvent display; /// Window event data 65 | #endif 66 | 67 | /* 68 | This is necessary for ABI compatibility between Visual C++ and GCC 69 | Visual C++ will respect the push pack pragma and use 52 bytes for 70 | this structure, and GCC will use the alignment of the largest datatype 71 | within the union, which is 8 bytes. 72 | 73 | So... we'll add padding to force the size to be 56 bytes for both. 74 | */ 75 | Uint8 padding[56]; 76 | 77 | ///Default ctor an event 78 | Event() 79 | { 80 | // statically checks the likelyness of this union to lineup with the C 81 | // union 82 | static_assert( 83 | sizeof(Event) == sizeof(SDL_Event), 84 | "Hey, it's likely that the bits in sdl::Event and SDL_Event don't " 85 | "lineup. Please check you are using a supported version of SDL2!!"); 86 | 87 | static_assert(offsetof(Event, common.timestamp) == offsetof(SDL_Event, common.timestamp)); 88 | 89 | static_assert(offsetof(Event, user.windowID) == offsetof(SDL_Event, user.windowID)); 90 | 91 | memset(this, 0, sizeof(SDL_Event)); 92 | } 93 | 94 | ///converting ctor to create an sdl::Event from an SDL_Event struct 95 | Event(SDL_Event const& e) : Event{*reinterpret_cast(&e)} {} 96 | 97 | ///Get a const reference to an sdl::Event from an SDL_Event 98 | static Event const& ref_from(SDL_Event const& e) { return *reinterpret_cast(&e); } 99 | 100 | ///Get a non-const reference to an sdl::Event from an SDL_Event 101 | static Event& ref_from(SDL_Event& e) { return *reinterpret_cast(&e); } 102 | 103 | /// \copydoc Event const& ref_from(SDL_Event const* e) 104 | static Event const& ref_from(SDL_Event const* e) { return *reinterpret_cast(e); } 105 | 106 | /// \copydoc Event& ref_from(SDL_Event& e) 107 | static Event& ref_from(SDL_Event* e) { return *reinterpret_cast(e); } 108 | 109 | /// Implicit convertion to SDL_Event() 110 | operator SDL_Event() const { return *reinterpret_cast(this); } 111 | 112 | /// Get a pointer to an SDL_Event 113 | SDL_Event const* ptr() const { return reinterpret_cast(this); } 114 | 115 | /// Get a pointer to an SDL_Event 116 | SDL_Event* ptr() { return reinterpret_cast(this); } 117 | 118 | ///For type safety, we will use these scoped enum values instead of raw numbers like the C api 119 | enum class State : int 120 | { 121 | Query = SDL_QUERY, 122 | Ignore = SDL_IGNORE, 123 | Enable = SDL_ENABLE, 124 | }; 125 | 126 | ///Pool for events, return false when there are no more events to poll 127 | bool poll() { return SDL_PollEvent(ptr()); } 128 | 129 | ///Wait until next event occur. This will stop the execution of your code until *something* happens 130 | void wait() 131 | { 132 | if (!SDL_WaitEvent(ptr())) throw Exception{"SDL_WaitEvent"}; 133 | } 134 | 135 | ///Wait until next event occur, or until the given duration expired 136 | /// \param timeout max duration to wait for in milliseconds 137 | void wait(int timeout) 138 | { 139 | if (!SDL_WaitEventTimeout(ptr(), timeout)) 140 | { 141 | throw Exception{"SDL_WaitEventTimeout"}; 142 | } 143 | } 144 | 145 | ///Push the current event to the list of event to process 146 | void push() const 147 | { 148 | // SDL_PushEvent won't modify it's argument 149 | if (!SDL_PushEvent(const_cast(ptr()))) 150 | { 151 | throw Exception{"SDL_PushEvent"}; 152 | } 153 | } 154 | 155 | /// Peek the next event in the list 156 | void peek() 157 | { 158 | if (SDL_PeepEvents(ptr(), 1, SDL_PEEKEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT) < 0) 159 | { 160 | throw Exception{"SDL_PeepEvents"}; 161 | } 162 | } 163 | 164 | ///Return true if there are events in the queue 165 | inline bool has_events() { return SDL_HasEvents(SDL_FIRSTEVENT, SDL_LASTEVENT); } 166 | 167 | ///Return true if there are events of a specific type in the queue 168 | ///\param type the type of the events you want to check for 169 | inline bool has_events(Uint32 type) { return SDL_HasEvent(type); } 170 | 171 | ///Return true if there are events of a specific range of types in the queue 172 | ///\param minType lower type boundary of the range 173 | ///\param maxType upper type boundary of the range 174 | inline bool has_events(Uint32 minType, Uint32 maxType) 175 | { 176 | return SDL_HasEvents(minType, maxType); 177 | } 178 | 179 | ///Pump the event loop from the OS event system. only call this from the main thread (or the thread that initialized the video/window systems) 180 | ///This is only usefull if you aren't polling or waiting for events 181 | inline void pump_events() { SDL_PumpEvents(); } 182 | 183 | ///Clear events of a range of types from the event queue 184 | ///\param minType lower type boundary of the range 185 | ///\param maxType upper type boundary of the range 186 | inline void flush_events(Uint32 minType, Uint32 maxType) { SDL_FlushEvents(minType, maxType); } 187 | 188 | ///Clear all events from the event queue 189 | inline void flush_events() { flush_events(SDL_FIRSTEVENT, SDL_LASTEVENT); } 190 | 191 | ///Clear events from a specific type from the event queue 192 | inline void flush_events(Uint32 type) { flush_events(type, type); } 193 | 194 | ///Add events of a specific range of types to the event queue 195 | ///\param events vector of events to be added 196 | ///\param minType lower type boundary of the range 197 | ///\param maxType upper type boundary of the range 198 | inline void add_events(std::vector const& events, Uint32 minType, Uint32 maxType) 199 | { 200 | // This use of SDL_PeepEvents don't modify the events 201 | auto array = const_cast(reinterpret_cast(&events[0])); 202 | if (SDL_PeepEvents(array, int(events.size()), SDL_ADDEVENT, minType, maxType) < 0) 203 | { 204 | throw Exception{"SDL_PeepEvents"}; 205 | } 206 | } 207 | 208 | ///Add events to the queue 209 | ///\param events vector of events to be added 210 | inline void add_events(std::vector const& events) 211 | { 212 | add_events(events, SDL_FIRSTEVENT, SDL_LASTEVENT); 213 | } 214 | 215 | ///Add events of a specific type to the queue 216 | ///\param events vector of events to be added 217 | ///\param type type of events to be added 218 | inline void add_events(std::vector const& events, Uint32 type) 219 | { 220 | add_events(events, type, type); 221 | } 222 | 223 | ///Peek at multiple future events 224 | ///\param maxEvents max number of events to get 225 | ///\param minType lower bound of event type range 226 | ///\param maxType upper bound of event type range 227 | inline std::vector peek_events(size_t maxEvents, Uint32 minType, Uint32 maxType) 228 | { 229 | auto res = std::vector(maxEvents); 230 | auto array = reinterpret_cast(&res[0]); 231 | if (SDL_PeepEvents(array, int(maxEvents), SDL_PEEKEVENT, minType, maxType) < 0) 232 | { 233 | throw Exception{"SDL_PeepEvents"}; 234 | } 235 | return res; 236 | } 237 | 238 | ///Peek at future events 239 | inline std::vector peek_events(size_t maxEvents) 240 | { 241 | return peek_events(maxEvents, SDL_FIRSTEVENT, SDL_LASTEVENT); 242 | } 243 | 244 | ///Peek events from a specific type 245 | ///\param type The type of events to look for 246 | inline std::vector peek_events(size_t maxEvents, Uint32 type) 247 | { 248 | return peek_events(maxEvents, type, type); 249 | } 250 | 251 | ///Get events from the queue 252 | ///\prarm maxEvents max number of events to get 253 | ///\param minType lower bound of type range 254 | ///\param maxType upper bound of type range 255 | inline std::vector get_events(size_t maxEvents, Uint32 minType, Uint32 maxType) 256 | { 257 | auto res = std::vector(maxEvents); 258 | auto array = reinterpret_cast(&res[0]); 259 | if (SDL_PeepEvents(array, int(maxEvents), SDL_GETEVENT, minType, maxType) < 0) 260 | { 261 | throw Exception{"SDL_PeepEvents"}; 262 | } 263 | return res; 264 | } 265 | 266 | ///Get events from the queue 267 | ///\param type The type of events to look for 268 | ///\param maxEvents max number of events to get 269 | inline std::vector get_events(size_t maxEvents) 270 | { 271 | return get_events(maxEvents, SDL_FIRSTEVENT, SDL_LASTEVENT); 272 | } 273 | 274 | ///Get events from a specific type 275 | ///\param type The type of events to look for 276 | ///\param maxEvents max number of events to get 277 | inline std::vector get_events(size_t maxEvents, Uint32 type) 278 | { 279 | return get_events(maxEvents, type, type); 280 | } 281 | 282 | ///Event filter object 283 | struct EventFilter 284 | { 285 | using func_type = bool (*)(void*, Event&); 286 | 287 | void* userdata_ = nullptr; 288 | func_type filter_ = nullptr; 289 | bool isWatcher_ = false; 290 | 291 | EventFilter(func_type filter, void* userdata) : filter_{filter}, userdata_{userdata} {} 292 | 293 | EventFilter(func_type filter) : filter_{filter} {} 294 | 295 | ~EventFilter() 296 | { 297 | if (isWatcher_) del_watcher(); 298 | } 299 | 300 | static int call_filter(void* data, SDL_Event* event) 301 | { 302 | auto filter = static_cast(data); 303 | return filter->filter_(filter->userdata_, sdl::Event::ref_from(event)); 304 | } 305 | 306 | void filter_queue() { SDL_FilterEvents(&call_filter, this); } 307 | 308 | void set() { SDL_SetEventFilter(&call_filter, userdata_); } 309 | 310 | static void unset() { SDL_FilterEvents(nullptr, nullptr); } 311 | 312 | void add_watcher() 313 | { 314 | SDL_AddEventWatch(&call_filter, this); 315 | isWatcher_ = true; 316 | } 317 | 318 | void del_watcher() 319 | { 320 | SDL_DelEventWatch(&call_filter, this); 321 | isWatcher_ = false; 322 | } 323 | }; 324 | 325 | inline Event::State event_state(Uint32 type) 326 | { 327 | return static_cast(SDL_GetEventState(type)); 328 | } 329 | 330 | inline void set_event_state(Uint32 type, Event::State state) 331 | { 332 | SDL_EventState(type, int(state)); 333 | } 334 | }; 335 | } // namespace sdl 336 | 337 | #include 338 | 339 | #if _MSC_VER >= 1910 340 | #pragma warning(pop) 341 | #endif 342 | -------------------------------------------------------------------------------- /sources/cpp-sdl2/exception.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | ///Define to deactivate exception support 11 | #ifdef CPP_SDL2_DISABLE_EXCEPTIONS 12 | #include //Instead of throwing, we are going to dump the function and error into stderr via std::cerr 13 | #endif 14 | 15 | namespace sdl 16 | { 17 | ///Define to deactivate exception support 18 | #ifndef CPP_SDL2_DISABLE_EXCEPTIONS 19 | 20 | ///Custom exception object 21 | class Exception : public std::exception 22 | { 23 | public: 24 | ///Construct an exception object. Will get the content of SDL_GetError() ont the '.what()' message 25 | ///\param function string containing the name of the SDL function 26 | Exception(std::string const& function) : function_{function}, error_{SDL_GetError()} 27 | { 28 | SDL_ClearError(); 29 | } 30 | 31 | ///Return the string containing the name of the SDL function that failed 32 | std::string function() { return function_; } 33 | ///Return the error message generated by SDL_GetError() at event object construction 34 | std::string error() { return error_; } 35 | 36 | const char* what() const noexcept override 37 | { 38 | if (what_.empty()) 39 | { 40 | what_ = "function: "; 41 | what_ += function_; 42 | what_ += " SDL error: "; 43 | what_ += error_; 44 | } 45 | 46 | return what_.c_str(); 47 | } 48 | 49 | private: 50 | ///storage for function name string 51 | std::string function_; 52 | ///storage for SDL_GetError() return string 53 | std::string error_; 54 | ///storage for the "what()" string. string is dynamically built when calling what() const 55 | mutable std::string what_; 56 | }; 57 | 58 | #else // Do not use exceptions 59 | 60 | // Stub the 'throw' keyword 61 | #define throw 62 | 63 | ///Castrated exception class 64 | class Exception 65 | { 66 | public: 67 | ///Constructor that will dump error informations to stderr, then kill the program 68 | Exception(std::string const& function) 69 | { 70 | std::cerr << "Error in : " << function << "\n"; 71 | const auto error = SDL_GetError(); 72 | std::cerr << "Error : " << error << "\n"; 73 | abort(); 74 | } 75 | }; 76 | 77 | #endif 78 | 79 | } // namespace sdl 80 | -------------------------------------------------------------------------------- /sources/cpp-sdl2/game_controller.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "exception.hpp" 4 | #include "haptic.hpp" 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | namespace sdl 12 | { 13 | ///\brief Represent a gamepad (game controller) 14 | class GameController 15 | { 16 | public: 17 | ///Construct a controller from a joystick index, throws if that index is not a game controller 18 | GameController(int joystick_index) : controller_(SDL_GameControllerOpen(joystick_index)) 19 | { 20 | if (!controller_) 21 | { 22 | throw Exception("SDL_GameControllerOpen"); 23 | } 24 | } 25 | 26 | ///Construct a controller from a GameController pointer. This object will take ownership of the controller pointed. 27 | GameController(SDL_GameController* controller) : controller_(controller) {} 28 | 29 | ///Construct an empty controller 30 | GameController() = default; 31 | 32 | ///Not copyable 33 | GameController(GameController const&) = delete; 34 | GameController& operator=(GameController const&) = delete; 35 | 36 | ///move ctor 37 | GameController(GameController&& other) noexcept { *this = std::move(other); } 38 | 39 | ///move-assing operator 40 | GameController& operator=(GameController&& other) noexcept 41 | { 42 | if (controller_ != other.controller_) 43 | { 44 | controller_ = other.ptr(); 45 | // We never want the value of the "owned_" boolean to change in the 46 | // life of one of these objects. however, in case of a move 47 | // operation, we need to transfer the owning status of the wrapper 48 | // In the general case, we would only want to rely on the fact that 49 | // the pointer is null or not. But here, we want to be able to 50 | // easily construct and manipulate a pointer that is not managed as 51 | // an RAAI object. This is ugly, and I'm upset about it. But we're 52 | // gonna cast-away const for once 53 | *(const_cast(&owned_)) = other.owned_; 54 | *(const_cast(&other.owned_)) = false; 55 | other.controller_ = nullptr; 56 | } 57 | return *this; 58 | } 59 | 60 | ///Close the opened controller pointer, unless this wrapper was created via GameController::non_owning() 61 | ~GameController() 62 | { 63 | if (owned_ && controller_) SDL_GameControllerClose(controller_); 64 | } 65 | 66 | ///Get the SDL pointer 67 | SDL_GameController* ptr() const { return controller_; } 68 | 69 | ///Open the haptic device from the controller 70 | Haptic open_haptic() const { return {SDL_GameControllerGetJoystick(controller_)}; } 71 | 72 | ///Return true if this controller is attached 73 | bool is_attached() const { return SDL_GameControllerGetAttached(controller_) == SDL_TRUE; } 74 | 75 | ///Get the current imediate value of the given axis 76 | int16_t get_axis(SDL_GameControllerAxis axis) const 77 | { 78 | return SDL_GameControllerGetAxis(controller_, axis); 79 | } 80 | 81 | ///Get the current imedate value of the given button 82 | int8_t get_button(SDL_GameControllerButton button) const 83 | { 84 | return SDL_GameControllerGetButton(controller_, button); 85 | } 86 | 87 | #if SDL_VERSION_ATLEAST(2, 0, 9) 88 | ///Play a simple rumble. If the controller has 2 motors, the two values will control one of them. If the controller only has one, the values will be mixed together 89 | int rumble(uint16_t low_freq, uint16_t high_freq, std::chrono::milliseconds duration) const 90 | { 91 | return rumble(low_freq, high_freq, static_cast(duration.count())); 92 | } 93 | 94 | ///\copydoc GameController::rumble 95 | int rumble(uint16_t low_freq, uint16_t high_freq, uint32_t millisec_duration) const 96 | { 97 | return SDL_GameControllerRumble(controller_, low_freq, high_freq, millisec_duration); 98 | } 99 | #endif 100 | 101 | std::string name() const 102 | { 103 | if (!controller_) return {}; 104 | 105 | return {SDL_GameControllerName(controller_)}; 106 | } 107 | 108 | static std::string get_controller_name(int joystick_index) 109 | { 110 | const char* name = SDL_GameControllerNameForIndex(joystick_index); 111 | if (!name) throw Exception("SDL_GameControllerNameForIndex"); 112 | return {name}; 113 | } 114 | 115 | // static std::string get_axis_name(SDL_GameControllerAxis axis) 116 | //{ 117 | // //todo error when SDL return null 118 | // return { SDL_GameControllerGetStringForAxis(axis) }; 119 | //} 120 | 121 | // static std::string get_button_name(SDL_GameControllerButton button) 122 | //{ 123 | // //todo error when SDL return null 124 | // return { SDL_GameControllerGetStringForButton(button) }; 125 | //} 126 | 127 | // Bindings management wrappers 128 | ///Load a file database 129 | static int load_mapping_database(std::string const& file_path) 130 | { 131 | return load_mapping_database(file_path.c_str()); 132 | } 133 | 134 | ///Load a file database 135 | static int load_mapping_database(const char* file_path) 136 | { 137 | const auto state = SDL_GameControllerAddMappingsFromFile(file_path); 138 | if (state < 0) 139 | { 140 | throw Exception("SDL_GameControllerAddMappingsFromFile"); 141 | } 142 | 143 | return state; 144 | } 145 | 146 | ///Add a mapping string 147 | static int add_mapping(std::string const& mapping_string) 148 | { 149 | return add_mapping(mapping_string.c_str()); 150 | } 151 | 152 | ///Add a mapping string 153 | static int add_mapping(const char* mapping_string) 154 | { 155 | const auto state = SDL_GameControllerAddMapping(mapping_string); 156 | 157 | if (state < 0) 158 | { 159 | throw Exception("SDL_GameControllerAddMapping"); 160 | } 161 | 162 | return state; 163 | } 164 | 165 | // convinience functions 166 | ///Try to open all available controllers, and return an array of all controller sucessfully openned 167 | static std::vector open_all_available_controllers() 168 | { 169 | std::vector controllers; 170 | 171 | for (int nb_sticks = SDL_NumJoysticks(), i = 0; i < nb_sticks; ++i) 172 | { 173 | if (SDL_IsGameController(i)) 174 | { 175 | try 176 | { 177 | controllers.emplace_back(i); 178 | } 179 | catch (sdl::Exception const& /*e*/) 180 | { 181 | // could not open this controller 182 | continue; 183 | } 184 | } 185 | } 186 | 187 | return controllers; 188 | } 189 | 190 | ///Create a non_owning controller around a stick ID, to use the C++ API without managing the controller 191 | static GameController non_owning(SDL_JoystickID joystick_id) 192 | { 193 | return {SDL_GameControllerFromInstanceID(joystick_id), false}; 194 | } 195 | 196 | ///Create a non_owning controller around an SDL controller pointer, to use the C++ aPI withiout managing the controller 197 | static GameController non_owning(SDL_GameController* controller) { return {controller, false}; } 198 | 199 | private: 200 | ///Private controller for a non-onwer controller. The bool argument is expected to be false here 201 | GameController(SDL_GameController* controller, bool non_owned) 202 | : controller_(controller), owned_(non_owned) 203 | { 204 | assert(!owned_); 205 | } 206 | 207 | SDL_GameController* controller_ = nullptr; 208 | bool const owned_ = true; 209 | }; 210 | } // namespace sdl 211 | -------------------------------------------------------------------------------- /sources/cpp-sdl2/haptic.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace sdl 7 | { 8 | class Haptic 9 | { 10 | ///Pointer to C sdl haptic device 11 | SDL_Haptic* haptic_ = nullptr; 12 | 13 | ///Type of an installed "effect" for SDL 14 | using effect_sdlid = int; 15 | 16 | ///Installed effect storage type 17 | using effect_list = std::vector; 18 | 19 | ///Installed effect storage 20 | effect_list my_effects{}; 21 | 22 | public: 23 | ///The the C pointer 24 | SDL_Haptic* ptr() const { return haptic_; } 25 | 26 | ///Installed effect handle 27 | class InstalledEffect 28 | { 29 | static constexpr effect_list::size_type invalid_index = 30 | std::numeric_limits::max(); 31 | effect_list::size_type index_ = invalid_index; 32 | Haptic* owner_ = nullptr; 33 | friend class Haptic; 34 | 35 | void move_from(InstalledEffect& other) {} 36 | 37 | public: 38 | InstalledEffect(effect_list::size_type index, Haptic* owner) : index_{index}, owner_{owner} 39 | { 40 | } 41 | 42 | InstalledEffect() = default; 43 | InstalledEffect(InstalledEffect const&); 44 | InstalledEffect& operator=(InstalledEffect const&); 45 | 46 | InstalledEffect(InstalledEffect&& other) noexcept { *this = std::move(other); } 47 | 48 | InstalledEffect& operator=(InstalledEffect&& other) noexcept 49 | { 50 | if (index_ != other.index_) 51 | { 52 | index_ = other.index_; 53 | owner_ = other.owner_; 54 | 55 | other.index_ = invalid_index; 56 | other.owner_ = nullptr; 57 | } 58 | return *this; 59 | } 60 | 61 | ~InstalledEffect() 62 | { 63 | if (owner_ && index_ != invalid_index) 64 | { 65 | const effect_sdlid my_real_id = owner_->get_effect_sdlid(*this); 66 | SDL_HapticDestroyEffect(owner_->ptr(), my_real_id); 67 | 68 | owner_->remove_effect(my_real_id); 69 | } 70 | } 71 | 72 | void run(uint32_t iterations = 1) { owner_->run_effect(*this, iterations); } 73 | }; 74 | 75 | #if _MSC_VER >= 1910 76 | #pragma warning(push) 77 | #pragma warning(disable : 26495) 78 | #endif 79 | 80 | ///Effect defintion 81 | union Effect 82 | { 83 | // This is analog to how we handle sdl events: this 84 | // definition is copied from SDL_haptic.h 85 | // note : It has been planned by SDL developers to change the `type` 86 | // type in 2.1.0. 87 | Uint16 type; /// Effect type. 88 | SDL_HapticConstant constant; /// Constant effect. 89 | SDL_HapticPeriodic periodic; /// Periodic effect. 90 | SDL_HapticCondition condition; /// Condition effect. 91 | SDL_HapticRamp ramp; /// Ramp effect. 92 | SDL_HapticLeftRight leftright; /// Left/Right effect. 93 | SDL_HapticCustom custom; /// Custom effect. 94 | 95 | ///this permit to treat an sdl::haptic::effect instance as if it was an SDL_HapticEffect pointer 96 | operator SDL_HapticEffect*() const 97 | { 98 | // "I solemnly swear that I am up to no good." 99 | return (SDL_HapticEffect*)(this); 100 | } 101 | 102 | ///Construct the effect. Fill it with zeroes 103 | Effect() 104 | { 105 | // Hopefully this will prevent the library to work when SDL will 106 | // decide to "fix" the "oops, the 'type' variable doesn't have 107 | // enough bits" problem they encouterd with 2.0 (this fix is 108 | // scheduled to be in 2.1 since it's an API/ABI breakage) 109 | static_assert( 110 | sizeof(Effect::type) == sizeof(SDL_HapticEffect::type), 111 | "please compare the layout between SDL_HapticEffect and " 112 | "sdl::Haptic::Effect"); 113 | static_assert( 114 | sizeof(Effect) == sizeof(SDL_HapticEffect), 115 | "please compare the layout between SDL_HapticEffect and " 116 | "sdl::Haptic::Effect"); 117 | 118 | // be sure to fully zero initialize the enclosed effect 119 | SDL_memset(this, 0, sizeof(Effect)); 120 | } 121 | }; 122 | 123 | #if _MSC_VER >= 1910 124 | #pragma warning(pop) 125 | #endif 126 | 127 | ///Uninitialized dummy haptic device 128 | Haptic() {} 129 | 130 | ///Open haptic device from index 131 | Haptic(int haptic_index) : haptic_{SDL_HapticOpen(haptic_index)} 132 | { 133 | if (!haptic_) 134 | { 135 | throw Exception("SDL_HapticOpen"); 136 | } 137 | } 138 | 139 | ///Open haptic device from joystick pointer 140 | Haptic(SDL_Joystick* joystick) : haptic_{SDL_HapticOpenFromJoystick(joystick)} 141 | { 142 | if (!haptic_) 143 | { 144 | throw Exception("SDL_HapticOpenFromJoystick"); 145 | } 146 | } 147 | 148 | ///close the haptic device automatically 149 | ~Haptic() 150 | { 151 | if (haptic_) 152 | { 153 | SDL_HapticClose(haptic_); 154 | } 155 | } 156 | 157 | // not copyable 158 | Haptic(Haptic const&) = delete; 159 | Haptic& operator=(Haptic const&) = delete; 160 | 161 | ///move ctor 162 | Haptic(Haptic&& other) noexcept { *this = std::move(other); } 163 | 164 | ///move assign opeartor 165 | Haptic& operator=(Haptic&& other) noexcept 166 | { 167 | if (haptic_ != other.haptic_) 168 | { 169 | haptic_ = other.haptic_; 170 | my_effects = std::move(other.my_effects); 171 | other.haptic_ = nullptr; 172 | } 173 | return *this; 174 | } 175 | 176 | ///Return true if the device is correctly opened 177 | bool valid() const { return haptic_ != nullptr; } 178 | 179 | ///Get a bitflag that describe the device capabilities 180 | unsigned int get_capabilities() const 181 | { 182 | if (!valid()) // we're not an opened device... So we should decide what 183 | // to do here... 184 | return 0U; 185 | 186 | const auto capabilities = SDL_HapticQuery(haptic_); 187 | 188 | if (!capabilities) 189 | { 190 | // couldn't get the capabilities of this haptic device. 191 | throw Exception("SDL_HapticQuery"); 192 | } 193 | 194 | return capabilities; 195 | } 196 | 197 | ///Check the SDL_HAPTIC_ flag agianst the device capabilities 198 | bool is_capable_of(int haptic_flag) const 199 | { 200 | const auto caps = get_capabilities(); 201 | return (haptic_flag & caps) != 0; 202 | } 203 | 204 | ///Install the effect 205 | InstalledEffect new_effect(Effect const& e) 206 | { 207 | // We need to be able to play the haptic effect 208 | if (!is_capable_of(e.type)) return {}; 209 | 210 | // maybe we should have another RAII wrapper around the registered 211 | // haptic effects 212 | const effect_sdlid raw_sdl_id = SDL_HapticNewEffect(haptic_, e); 213 | 214 | if (raw_sdl_id < 0) throw Exception("SDL_HapticNewEffect"); 215 | 216 | my_effects.push_back(raw_sdl_id); 217 | return {my_effects.size() - 1, this}; 218 | } 219 | 220 | ///Get the number of effects installed 221 | effect_list::size_type registered_effect_count() const { return my_effects.size(); } 222 | 223 | ///Get the SDL assigned ID (an integer) to the effect 224 | effect_sdlid get_effect_sdlid(InstalledEffect const& h) const 225 | { 226 | return my_effects.at(h.index_); 227 | } 228 | 229 | ///Deactiave the effect. This only set the registered id in question to -1 230 | void remove_effect(effect_sdlid e) 231 | { 232 | for (size_t i = 0, nb_effects = my_effects.size(); i < nb_effects; ++i) 233 | { 234 | if (my_effects[i] == e) my_effects[i] = -1; 235 | } 236 | } 237 | 238 | ///Run an effect, if said effect is valid 239 | void run_effect(InstalledEffect const& h, uint32_t iterations = 1) const 240 | { 241 | const effect_sdlid e = get_effect_sdlid(h); 242 | if (e >= 0 && SDL_HapticRunEffect(haptic_, get_effect_sdlid(h), iterations) < 0) 243 | { 244 | throw Exception("SDL_HapticRunEffect"); 245 | } 246 | } 247 | 248 | ///Check if you can safely attemp to install the effect ont he haptic device 249 | bool is_effect_compatible(Effect const& e) const { return is_capable_of(e.type); } 250 | }; 251 | } // namespace sdl 252 | -------------------------------------------------------------------------------- /sources/cpp-sdl2/joystick.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "exception.hpp" 4 | #include "haptic.hpp" 5 | #include "vec2.hpp" 6 | #include 7 | #include 8 | 9 | namespace sdl 10 | { 11 | class Joystick 12 | { 13 | SDL_Joystick* joystick_ = nullptr; 14 | const bool owner_ = true; 15 | 16 | ///Construct a non-owning joystick object 17 | Joystick(SDL_Joystick* stick, bool owning_state) : joystick_(stick), owner_(owning_state) 18 | { 19 | assert(!owner_); 20 | } 21 | 22 | public: 23 | ///Default ctor, create a non-valid (empty) joystick object 24 | Joystick() : owner_(false) {} 25 | 26 | ///Create a joystick by opening a joystick device 27 | Joystick(int device_index) : joystick_(SDL_JoystickOpen(device_index)) 28 | { 29 | if (!joystick_) throw Exception("SDL_JoystickOpen"); 30 | } 31 | 32 | ///Create a joystick from an SDL joystick 33 | Joystick(SDL_Joystick* joystick) : joystick_(joystick) {} 34 | 35 | Joystick(Joystick const&) = delete; 36 | Joystick& operator=(Joystick const&) = delete; 37 | 38 | ///move ctor 39 | Joystick(Joystick&& other) noexcept { *this = std::move(other); } 40 | 41 | ///move assign operator 42 | Joystick& operator=(Joystick&& other) noexcept 43 | { 44 | if (joystick_ 45 | != other.joystick_) // todo do we need to check owner_ here? need to think about that. 46 | { 47 | joystick_ = other.joystick_; 48 | other.joystick_ = nullptr; 49 | 50 | // this is the exceptional case when we actually want to reassign 51 | // the value of the "owner" boolean 52 | const_cast(owner_) = other.owner_; 53 | const_cast(other.owner_) = false; // for good measure 54 | } 55 | return *this; 56 | } 57 | 58 | ///Will automatically close the joystick when going out of scope if this object owns the pointer 59 | ~Joystick() 60 | { 61 | if (owner_ && joystick_) SDL_JoystickClose(joystick_); 62 | } 63 | 64 | ///Open haptics for this device 65 | Haptic open_haptic() const { return Haptic(joystick_); } 66 | 67 | ///Get the power level of the joystick. Will throw if unknown 68 | SDL_JoystickPowerLevel power_level() const 69 | { 70 | const auto power = SDL_JoystickCurrentPowerLevel(joystick_); 71 | 72 | if (power == SDL_JOYSTICK_POWER_UNKNOWN) throw Exception("SDL_JoystickCurrentPowerLevel"); 73 | 74 | return power; 75 | } 76 | 77 | ///Return true if device is currently attached 78 | bool attached() const { return SDL_JoystickGetAttached(joystick_) == SDL_TRUE; } 79 | 80 | ///Get the current immediate value of the given axis 81 | int16_t get_axis(int axis) const { return SDL_JoystickGetAxis(joystick_, axis); } 82 | 83 | ///Get the current immediate value of the given trackball 84 | sdl::Vec2i get_ball(int ball) const 85 | { 86 | Vec2i d; 87 | const int status = SDL_JoystickGetBall(joystick_, ball, &d.x, &d.y); 88 | 89 | if (status < 0) throw Exception("SDL_JoystickGetBall"); 90 | 91 | return d; 92 | } 93 | 94 | ///Get the current immediate value of the given button 95 | uint8_t get_button(int button) const { return SDL_JoystickGetButton(joystick_, button); } 96 | 97 | ///Get the current immediate value of the given hat 98 | uint8_t get_hat(int hat) const { return SDL_JoystickGetHat(joystick_, hat); } 99 | 100 | ///Get the name of the joystick 101 | std::string name() const { return {SDL_JoystickName(joystick_)}; } 102 | 103 | ///Get how many hats 104 | int num_hats() const 105 | { 106 | const int value = SDL_JoystickNumHats(joystick_); 107 | 108 | if (value < 0) throw Exception("SDL_JoystickNumHats"); 109 | 110 | return value; 111 | } 112 | 113 | ///Get how many buttons 114 | int num_buttons() const 115 | { 116 | const int value = SDL_JoystickNumButtons(joystick_); 117 | 118 | if (value < 0) throw Exception("SDL_JoystickNumButtons"); 119 | 120 | return value; 121 | } 122 | 123 | ///Get how many balls 124 | int num_balls() const 125 | { 126 | const int value = SDL_JoystickNumBalls(joystick_); 127 | 128 | if (value < 0) throw Exception("SDL_JoystickNumBalls"); 129 | 130 | return value; 131 | } 132 | 133 | ///Get how many axes 134 | int num_axes() const 135 | { 136 | const int value = SDL_JoystickNumAxes(joystick_); 137 | 138 | if (value < 0) throw Exception("SDL_JoystickNumAxes"); 139 | 140 | return value; 141 | } 142 | 143 | ///Get this joystick instance id 144 | SDL_JoystickID instance_id() const 145 | { 146 | const auto value = SDL_JoystickInstanceID(joystick_); 147 | 148 | if (value < 0) throw Exception("SDL_JoystickInstanceID"); 149 | 150 | return value; 151 | } 152 | 153 | ///construct a non-owning wrapper around this joystick 154 | static Joystick non_owning(SDL_Joystick* stick) { return {stick, false}; } 155 | 156 | ///construct a non-owning wrapper around this joystick id 157 | static Joystick non_joystick(SDL_JoystickID joyid) 158 | { 159 | auto object = Joystick(SDL_JoystickFromInstanceID(joyid), false); 160 | 161 | if (object.joystick_ == nullptr) throw Exception("SDL_JoystickFromInstanceID"); 162 | 163 | return object; 164 | } 165 | 166 | bool operator==(Joystick const& other) const { return joystick_ == other.joystick_; } 167 | 168 | bool operator==(SDL_Joystick* other) const { return joystick_ == other; } 169 | 170 | bool operator==(SDL_JoystickID other) const { return instance_id() == other; } 171 | }; 172 | } // namespace sdl 173 | -------------------------------------------------------------------------------- /sources/cpp-sdl2/mouse.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "exception.hpp" 4 | #include "surface.hpp" 5 | #include "window.hpp" 6 | #include 7 | 8 | namespace sdl::Mouse 9 | { 10 | ///Namespace containing everything that has to do with mouse management 11 | ///Set the mose in relative mode 12 | inline void set_relative(bool enabled) 13 | { 14 | if (SDL_SetRelativeMouseMode(enabled ? SDL_TRUE : SDL_FALSE) < 0) 15 | throw Exception("SDL_SetRelativeMouseMode"); 16 | } 17 | 18 | ///Get if the mouse is in relative mode 19 | inline bool get_relative() 20 | { 21 | return SDL_GetRelativeMouseMode() == SDL_TRUE; 22 | } 23 | 24 | ///Wrap inside the given windows 25 | inline void warp_in_window(Window const& window, Vec2i const& position) 26 | { 27 | SDL_WarpMouseInWindow(window.ptr(), position.x, position.y); 28 | } 29 | 30 | ///Wrap at this global and absolute position 31 | inline void warp_global(Vec2i const& position) 32 | { 33 | SDL_WarpMouseGlobal(position.x, position.y); 34 | } 35 | 36 | ///Cursor object 37 | class Cursor 38 | { 39 | ///SDL pointer 40 | SDL_Cursor* cursor_ = nullptr; 41 | 42 | public: 43 | ///Construct a system cursor 44 | Cursor(SDL_SystemCursor id) : cursor_(SDL_CreateSystemCursor(id)) 45 | { 46 | if (!cursor_) 47 | { 48 | throw Exception("SDL_CreateSystemCursor"); 49 | } 50 | } 51 | 52 | ///Construct a cursor from MSB bitmap data 53 | Cursor(const uint8_t* data, const uint8_t* mask, Vec2i const& size, Vec2i const& hot) 54 | : cursor_(SDL_CreateCursor(data, mask, size.x, size.y, hot.x, hot.y)) 55 | { 56 | if (!cursor_) 57 | { 58 | throw Exception("SDL_CreateCursor"); 59 | } 60 | } 61 | 62 | ///Create cursor from a SDL_Surface 63 | Cursor(Surface const& surface, Vec2i const& hot) 64 | : cursor_(SDL_CreateColorCursor(surface.ptr(), hot.x, hot.y)) 65 | { 66 | if (!cursor_) 67 | { 68 | throw Exception("SDL_CreateColorCursor"); 69 | } 70 | } 71 | 72 | ///Free the cursor 73 | ~Cursor() 74 | { 75 | if (cursor_) SDL_FreeCursor(cursor_); 76 | } 77 | 78 | Cursor& operator=(Cursor&& other) noexcept 79 | { 80 | if (cursor_ != other.cursor_) 81 | { 82 | cursor_ = other.cursor_; 83 | other.cursor_ = nullptr; 84 | } 85 | return *this; 86 | } 87 | 88 | Cursor(Cursor&& other) noexcept { *this = std::move(other); } 89 | 90 | ///Set this cursor as the current cursor 91 | void set() const 92 | { 93 | if (cursor_) SDL_SetCursor(cursor_); 94 | } 95 | 96 | ///Set the cursor as visible 97 | static void show() 98 | { 99 | const auto value = SDL_ShowCursor(SDL_ENABLE); 100 | 101 | if (value != SDL_ENABLE) 102 | { 103 | throw Exception("SDL_ShowCursor"); 104 | } 105 | } 106 | 107 | ///Set the cursor as invisible 108 | static void hide() 109 | { 110 | const auto value = SDL_ShowCursor(SDL_DISABLE); 111 | 112 | if (value != SDL_DISABLE) 113 | { 114 | throw Exception("SDL_ShowCursor"); 115 | } 116 | } 117 | 118 | ///Return true or false if the cursor is curently visible or not 119 | static bool visible() 120 | { 121 | const auto value = SDL_ShowCursor(SDL_QUERY); 122 | 123 | if (value < 0) 124 | { 125 | throw Exception("SDL_ShowCursor"); 126 | } 127 | 128 | return value == SDL_ENABLE; 129 | } 130 | }; 131 | } // namespace sdl::Mouse 132 | -------------------------------------------------------------------------------- /sources/cpp-sdl2/pixel.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "color.hpp" 4 | #include "exception.hpp" 5 | #include 6 | 7 | namespace sdl 8 | { 9 | ///Represent a 4 channel pixel 10 | class Pixel 11 | { 12 | public: 13 | ///Construct a pixel structure 14 | Pixel(void* target, SDL_PixelFormat const& fmt) : target_{target}, fmt_{fmt} {} 15 | 16 | ///Set color to this pixel via operator = 17 | Pixel& operator=(Color const& c) 18 | { 19 | set_color(c); 20 | return *this; 21 | } 22 | ///Set color to this pixel 23 | void set_color(Color const& c) { set_raw(c.as_uint(fmt_)); } 24 | 25 | ///Get color 26 | Color color() const { return Color{get_raw(), fmt_}; } 27 | 28 | ///Get red channel as byte 29 | Uint8 r() const { return color().r; } 30 | ///Get green channel as byte 31 | Uint8 g() const { return color().g; } 32 | ///Get blue channel as byte 33 | Uint8 b() const { return color().b; } 34 | ///Get alpha channel as byte 35 | Uint8 a() const { return color().a; } 36 | 37 | ///Set red channel from given byte 38 | void set_r(Uint8 r) 39 | { 40 | auto c = color(); 41 | c.r = r; 42 | set_color(c); 43 | } 44 | ///Set green channel from given byte 45 | void set_g(Uint8 g) 46 | { 47 | auto c = color(); 48 | c.g = g; 49 | set_color(c); 50 | } 51 | ///Set blue channel from given byte 52 | void set_b(Uint8 b) 53 | { 54 | auto c = color(); 55 | c.b = b; 56 | set_color(c); 57 | } 58 | ///Set alpha channel from given byte 59 | void set_a(Uint8 a) 60 | { 61 | auto c = color(); 62 | c.a = a; 63 | set_color(c); 64 | } 65 | 66 | private: 67 | ///Set this pixel from a 32byte value 68 | void set_raw(Uint32 raw) 69 | { 70 | switch (fmt_.BytesPerPixel) 71 | { 72 | case 1: *static_cast(target_) = static_cast(raw); break; 73 | case 2: *static_cast(target_) = static_cast(raw); break; 74 | case 3: 75 | if constexpr (SDL_BYTEORDER == SDL_BIG_ENDIAN) 76 | { 77 | static_cast(target_)[0] = static_cast((raw >> 16) & 0xFF); 78 | static_cast(target_)[1] = static_cast((raw >> 8) & 0xFF); 79 | static_cast(target_)[2] = static_cast(raw & 0xFF); 80 | } 81 | else 82 | { 83 | static_cast(target_)[0] = static_cast(raw & 0xFF); 84 | static_cast(target_)[1] = static_cast((raw >> 8) & 0xFF); 85 | static_cast(target_)[2] = static_cast((raw >> 16) & 0xFF); 86 | } 87 | break; 88 | case 4: *static_cast(target_) = raw; break; 89 | } 90 | } 91 | ///Get pixel as 32 bit value 92 | Uint32 get_raw() const 93 | { 94 | switch (fmt_.BytesPerPixel) 95 | { 96 | case 1: return *static_cast(target_); 97 | case 2: return *static_cast(target_); 98 | case 3: 99 | if constexpr (SDL_BYTEORDER == SDL_BIG_ENDIAN) 100 | { 101 | return static_cast(target_)[0] << 16 | static_cast(target_)[1] << 8 102 | | static_cast(target_)[2]; 103 | } 104 | else 105 | { 106 | return static_cast(target_)[0] | static_cast(target_)[1] << 8 107 | | static_cast(target_)[2] << 16; 108 | } 109 | case 4: return *static_cast(target_); 110 | } 111 | 112 | // TODO decide if landing here is an important error and throw, or just return a black pixel 113 | return 0; 114 | } 115 | 116 | ///pointer to target structure 117 | void* target_; 118 | ///Used pixel format on this format 119 | const SDL_PixelFormat& fmt_; 120 | }; 121 | 122 | } // namespace sdl 123 | -------------------------------------------------------------------------------- /sources/cpp-sdl2/rect.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "vec2.hpp" 7 | 8 | namespace sdl 9 | { 10 | ///sdl::Rect, C++ wrapping of SDL_Rect 11 | class Rect : public SDL_Rect 12 | { 13 | public: 14 | ///Construct a Rect initialized at 0 15 | constexpr Rect() : SDL_Rect{0, 0, 0, 0} {} 16 | 17 | ///Construct a Rect with the given dimensions 18 | ///\param x Position on X axis 19 | ///\param y Position on Y axis 20 | ///\param w Size on X axis 21 | ///\param h Size on Y axis 22 | constexpr Rect(int x, int y, int w, int h) : SDL_Rect{x, y, w, h} {} 23 | 24 | ///Construct a Rect with the given dimensions 25 | ///\param corner X/Y position on screen as a 2D vector 26 | ///\param size X/Y size on screen as a 2D vector 27 | constexpr Rect(Vec2i const& corner, Vec2i const& size) 28 | : SDL_Rect{corner.x, corner.y, size.x, size.y} 29 | { 30 | } 31 | 32 | ///Copy a Rect 33 | constexpr Rect(SDL_Rect const& r) : SDL_Rect{r} {} 34 | 35 | ///Copy a Rect 36 | Rect(Rect const&) noexcept = default; 37 | 38 | ///Move from a Rect 39 | Rect(Rect&&) noexcept = default; 40 | 41 | ///Contruct a Rect with dimensions around a center point 42 | ///\param cx position of the center on the X axis 43 | ///\param cy position of the center on the Y axis 44 | ///\param w Size on X axis 45 | ///\param h Size on Y axis 46 | static constexpr Rect from_center(int cx, int cy, int w, int h) 47 | { 48 | return Rect{cx - w / 2, cy - h / 2, w, h}; 49 | } 50 | 51 | ///Construct a Rect with dimensions around a center point 52 | ///\param center X/Y position of the center point as a 2D vector 53 | static constexpr Rect from_center(Vec2i const& center, Vec2i const& size) 54 | { 55 | return Rect{center - size / 2, size}; 56 | } 57 | 58 | ///Construct a rect from 2 corner points 59 | ///\param x1 X position of first point 60 | ///\param y1 Y position of first point 61 | ///\param x2 X position of second point 62 | ///\param x2 Y position of second point 63 | static constexpr Rect from_corners(int x1, int y1, int x2, int y2) 64 | { 65 | return Rect(x1, y1, x2 - x1, y2 - y1); 66 | } 67 | 68 | ///Construct a rect from 2 corner points 69 | ///\param corner1 X/Y position of the first corner as a 2D vector 70 | ///\param corner2 X/Y position of the second corner as a 2D vector 71 | static constexpr Rect from_corners(Vec2i const& corner1, Vec2i const& corner2) 72 | { 73 | return Rect(corner1.x, corner1.y, corner2.x - corner1.x, corner2.y - corner1.y); 74 | } 75 | 76 | ///Copy assign a Rect 77 | Rect& operator=(Rect const&) noexcept = default; 78 | ///Move assign a Rect 79 | Rect& operator=(Rect&&) noexcept = default; 80 | 81 | ///Returns true if the two rect are the same 82 | bool operator==(Rect const& other) const { return SDL_RectEquals(this, &other); } 83 | 84 | ///Return the 'min X' position of the Rect 85 | constexpr int x1() const { return x; } 86 | ///Return the 'max X' position of the Rect 87 | constexpr int x2() const { return x + w; } 88 | ///Return the 'min Y' position of the Rect 89 | constexpr int y1() const { return y; } 90 | ///Return the 'max Y' position of the Rect 91 | constexpr int y2() const { return y + h; } 92 | 93 | ///Get the bottom left corner position 94 | Vec2i botleft() const { return Vec2i{x1(), y1()}; } 95 | ///Get the bottom right corner position 96 | Vec2i botright() const { return Vec2i{x2(), y1()}; } 97 | ///Get the top left corner position 98 | Vec2i topleft() const { return Vec2i{x1(), y2()}; } 99 | ///Get the top right corner position 100 | Vec2i topright() const { return Vec2i{x2(), y2()}; } 101 | 102 | ///Get the size of the Rect 103 | Vec2i size() const { return Vec2i{w, h}; } 104 | ///Get the center of the Rect 105 | Vec2i center() const { return Vec2i{x + w / 2, y + h / 2}; } 106 | 107 | ///Return true if this Rect is empty 108 | bool is_empty() const { return SDL_RectEmpty(this); } 109 | 110 | ///Return true if this rect contains the given point 111 | ///\param px X position of the point 112 | ///\param py Y position of the point 113 | bool contains(int px, int py) const 114 | { 115 | return px >= x1() && px < x2() && py >= y1() && py < y2(); 116 | } 117 | 118 | ///Return true if this rect contains the given point 119 | ///\param point The X/Y coordinates of the point as a vector 2D 120 | bool contains(Vec2i const& point) const { return contains(point.x, point.y); } 121 | 122 | ///Return true if this rect intersect another rect 123 | bool intersects(Rect const& r) const 124 | { 125 | return x1() < r.x2() && x2() > r.x1() && y1() < r.y2() && y2() > r.y1(); 126 | } 127 | 128 | ///Return true if this rect intersect the line 129 | bool intersects(Vec2i const& p1, Vec2i const& p2) const 130 | { 131 | /* Even if SDL_IntersectRectAndLine don't modify it's arguments, 132 | it doesn't use const* of int */ 133 | auto p1mut = const_cast(p1); 134 | auto p2mut = const_cast(p2); 135 | 136 | return SDL_IntersectRectAndLine(this, &p1mut.x, &p1mut.y, &p2mut.x, &p2mut.y); 137 | } 138 | 139 | ///Return the intersection of the two rects 140 | Rect inter(Rect const& r) const 141 | { 142 | Rect tmp; 143 | SDL_IntersectRect(this, &r, &tmp); 144 | return tmp; 145 | } 146 | 147 | // TODO : find a way to name this ... 148 | ///Return the union of the two rects 149 | Rect get_union(Rect const& r) const 150 | { 151 | Rect tmp; 152 | SDL_UnionRect(this, &r, &tmp); 153 | return tmp; 154 | } 155 | }; 156 | 157 | } // namespace sdl 158 | -------------------------------------------------------------------------------- /sources/cpp-sdl2/renderer.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "color.hpp" 4 | #include "exception.hpp" 5 | #include "rect.hpp" 6 | #include "surface.hpp" 7 | #include "texture.hpp" 8 | 9 | #include 10 | #include 11 | 12 | #include 13 | #include 14 | 15 | namespace sdl 16 | { 17 | ///Class that represent a SDL2 2D renderer 18 | class Renderer 19 | { 20 | public: 21 | ///Construct a renderer from the SDL_Renderer C object 22 | explicit Renderer(SDL_Renderer* renderer) : renderer_{renderer} {} 23 | 24 | ///Default ctor, create an empty renderer object 25 | Renderer() {} 26 | 27 | ///Default move ctor 28 | Renderer(Renderer&& other) noexcept { *this = std::move(other); } 29 | 30 | ///Move a renderer to this one 31 | Renderer& operator=(Renderer&& other) noexcept 32 | { 33 | if (renderer_ != other.renderer_) 34 | { 35 | SDL_DestroyRenderer(renderer_); 36 | renderer_ = other.renderer_; 37 | other.renderer_ = nullptr; 38 | } 39 | return *this; 40 | } 41 | 42 | ///Destroy the renderer automaticlally when object goes out of scope 43 | ~Renderer() { SDL_DestroyRenderer(renderer_); } 44 | 45 | ///This is a managed RAII resource. this object is not copyable 46 | Renderer(Renderer const&) = delete; 47 | 48 | ///This is a managed RAII resource. this object is not copyable 49 | Renderer& operator=(Renderer const&) = delete; 50 | 51 | ///Return a pointer to the wrapped C SDL_Renderer 52 | SDL_Renderer* ptr() const { return renderer_; } 53 | 54 | ///Populate renderinfo structure 55 | void get_info(SDL_RendererInfo& info) const 56 | { 57 | if (SDL_GetRendererInfo(renderer_, &info) != 0) throw Exception{"SDL_GetRendererInfo"}; 58 | } 59 | 60 | ///Get renderer infos 61 | SDL_RendererInfo info() const 62 | { 63 | SDL_RendererInfo info; 64 | get_info(info); 65 | return info; 66 | } 67 | 68 | // Get the current draw color 69 | Color drawcolor() const 70 | { 71 | Color c; 72 | if (SDL_GetRenderDrawColor(renderer_, &c.r, &c.g, &c.b, &c.a) != 0) 73 | throw Exception{"SDL_GetRenderDrawColor"}; 74 | return c; 75 | } 76 | 77 | ///Set the drawcolor from color values as bytes 78 | void set_drawcolor(Uint8 r, Uint8 g, Uint8 b, Uint8 a = SDL_ALPHA_OPAQUE) const 79 | { 80 | if (SDL_SetRenderDrawColor(renderer_, r, g, b, a) != 0) 81 | throw Exception{"SDL_SetRenderDrawColor"}; 82 | } 83 | 84 | ///Set the drawcolor from color struct 85 | void set_drawcolor(Color const& c) const { set_drawcolor(c.r, c.g, c.b, c.a); } 86 | 87 | ///Get the clipping rectangle 88 | Rect cliprect() const 89 | { 90 | Rect r; 91 | SDL_RenderGetClipRect(renderer_, &r); 92 | return r; 93 | } 94 | 95 | ///Set the clipping rectangle 96 | void set_cliprect(Rect const& r) const 97 | { 98 | if (SDL_RenderSetClipRect(renderer_, &r) != 0) throw Exception{"SDL_RenderSetClipRect"}; 99 | } 100 | 101 | ///Return true if clipping is enable 102 | bool clip_enabled() const { return SDL_RenderIsClipEnabled(renderer_); } 103 | 104 | ///Disable the clipping rectangle 105 | void disable_clip() const 106 | { 107 | if (SDL_RenderSetClipRect(renderer_, nullptr) != 0) 108 | throw Exception{"SDL_RenderSetClipRect"}; 109 | } 110 | 111 | ///Get the current integer scale 112 | bool intscale() const { return SDL_RenderGetIntegerScale(renderer_); } 113 | 114 | ///Set the integer scale 115 | void set_intscale(bool intscale) const 116 | { 117 | if (SDL_RenderSetIntegerScale(renderer_, SDL_bool(intscale)) != 0) 118 | throw Exception{"SDL_RenderSetIntegerScale"}; 119 | } 120 | 121 | ///Make a new texture 122 | Texture make_texture(Uint32 format, SDL_TextureAccess access, int w, int h) const 123 | { 124 | return Texture{renderer_, format, access, w, h}; 125 | } 126 | 127 | ///Make a new texture 128 | Texture make_texture(Uint32 format, SDL_TextureAccess access, Vec2i size) const 129 | { 130 | return Texture{renderer_, format, access, size.x, size.y}; 131 | } 132 | 133 | ///Make a texture from a surface 134 | Texture make_texture(Surface const& surface) const { return Texture{renderer_, surface}; } 135 | 136 | ///Make a texture from a file 137 | ///\param filename file path 138 | Texture make_texture(std::string const& filename) const { return Texture{renderer_, filename}; } 139 | 140 | void render_copy(Texture const& tex, Rect const& source_rect, Rect const& dest_rect) const 141 | { 142 | // SDL will *not* modify the texture or the rects here, but the 143 | // signature has a non-const pointer So we are forced to cast away our 144 | // const ref on texture 145 | SDL_RenderCopy(renderer_, const_cast(tex).ptr(), &source_rect, &dest_rect); 146 | } 147 | 148 | ///Present renderer 149 | void present() const { SDL_RenderPresent(renderer_); } 150 | 151 | ///Clear renderer 152 | void clear() const 153 | { 154 | if (SDL_RenderClear(renderer_) != 0) throw Exception{"SDL_RenderClear"}; 155 | } 156 | 157 | ///Clear with specified color 158 | void clear(Color const& c) const 159 | { 160 | set_drawcolor(c); 161 | clear(); 162 | } 163 | 164 | ///Draw line between two points 165 | void draw_line(Vec2i const& pos1, Vec2i const& pos2) const 166 | { 167 | if (SDL_RenderDrawLine(renderer_, pos1.x, pos1.y, pos2.x, pos2.y) != 0) 168 | throw Exception{"SDL_RenderDrawLine"}; 169 | } 170 | 171 | ///Draw line between two points with specified color 172 | void draw_line(Vec2i const& pos1, Vec2i const& pos2, Color const& c) const 173 | { 174 | set_drawcolor(c); 175 | draw_line(pos1, pos2); 176 | } 177 | 178 | ///Draw array of lines 179 | void draw_lines(std::vector const& points) const 180 | { 181 | if (SDL_RenderDrawLines(renderer_, &points[0], (int)points.size()) != 0) 182 | throw Exception{"SDL_RenderDrawLines"}; 183 | } 184 | 185 | ///Draw array of lines with specified color 186 | void draw_lines(std::vector const& points, Color const& c) const 187 | { 188 | set_drawcolor(c); 189 | draw_lines(points); 190 | } 191 | 192 | ///Draw point 193 | void draw_point(Vec2i const& point) const 194 | { 195 | if (SDL_RenderDrawPoint(renderer_, point.x, point.y) != 0) 196 | throw Exception{"SDL_RenderDrawPoint"}; 197 | } 198 | 199 | ///Draw point with specified color 200 | void draw_point(Vec2i const& point, Color const& c) const 201 | { 202 | set_drawcolor(c); 203 | draw_point(point); 204 | } 205 | 206 | ///Draw array of points 207 | void draw_points(std::vector const& points) const 208 | { 209 | if (SDL_RenderDrawPoints(renderer_, &points[0], (int)points.size()) != 0) 210 | throw Exception{"SDL_RenderDrawPoints"}; 211 | } 212 | 213 | ///Draw array of points with specified color 214 | void draw_points(std::vector const& points, Color const& c) const 215 | { 216 | set_drawcolor(c); 217 | draw_points(points); 218 | } 219 | 220 | void draw_ray(Vec2i const& orig, Vec2i const& ray) const { draw_line(orig, orig + ray); } 221 | void draw_ray(Vec2i const& orig, Vec2i const& ray, Color const& c) const 222 | { 223 | draw_line(orig, orig + ray, c); 224 | } 225 | 226 | ///Draw rectangle 227 | void draw_rect(Rect const& rect) const 228 | { 229 | if (SDL_RenderDrawRect(renderer_, &rect) != 0) throw Exception{"SDL_RenderDrawRect"}; 230 | } 231 | 232 | ///Draw rectangle with specified color 233 | void draw_rect(Rect const& rect, Color const& c) const 234 | { 235 | set_drawcolor(c); 236 | draw_rect(rect); 237 | } 238 | 239 | ///Draw array of rectangles 240 | void draw_rects(std::vector const& rects) const 241 | { 242 | if (SDL_RenderDrawRects(renderer_, &rects[0], (int)rects.size()) != 0) 243 | throw Exception{"SDL_RenderDrawRects"}; 244 | } 245 | ///Draw array of rectangles with specified colors 246 | void draw_rects(std::vector const& rects, const Color& c) const 247 | { 248 | set_drawcolor(c); 249 | draw_rects(rects); 250 | } 251 | 252 | ///Fill rectangle 253 | void fill_rect(Rect const& rect) const 254 | { 255 | if (SDL_RenderFillRect(renderer_, &rect) != 0) throw Exception{"SDL_RenderFillRect"}; 256 | } 257 | ///Fill rectangle with specified color 258 | void fill_rect(Rect const& rect, Color const& c) const 259 | { 260 | set_drawcolor(c); 261 | fill_rect(rect); 262 | } 263 | 264 | ///Fill array of rectangles 265 | void fill_rects(std::vector const& rects) const 266 | { 267 | if (SDL_RenderFillRects(renderer_, &rects[0], (int)rects.size()) != 0) 268 | throw Exception{"SDL_RenderDrawRects"}; 269 | } 270 | ///Fill array of rectangles with specified colors 271 | void fill_rects(std::vector const& rects, Color const& c) 272 | { 273 | set_drawcolor(c); 274 | fill_rects(rects); 275 | } 276 | 277 | private: 278 | ///Pointer to raw SDL_Renderer 279 | SDL_Renderer* renderer_ = nullptr; 280 | }; 281 | 282 | } // namespace sdl 283 | -------------------------------------------------------------------------------- /sources/cpp-sdl2/sdl.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "color.hpp" 6 | #include "event.hpp" 7 | #include "exception.hpp" 8 | #include "game_controller.hpp" 9 | #include "haptic.hpp" 10 | #include "joystick.hpp" 11 | #include "mouse.hpp" 12 | #include "rect.hpp" 13 | #include "renderer.hpp" 14 | #include "shared_object.hpp" 15 | #include "simd.hpp" 16 | #include "surface.hpp" 17 | #include "texture.hpp" 18 | #include "timer.hpp" 19 | #include "utils.hpp" 20 | #include "vec2.hpp" 21 | #include "window.hpp" 22 | 23 | /** 24 | * 25 | * \mainpage cpp-sdl2 header-only C++ bindings for SDL2 26 | * 27 | * 28 | * \section doc_intro Introduction 29 | * 30 | * SDL2 is arguably one of the most helpfull free and open source libraries. As 31 | * it is both a common API for many platform and operating systems to do 32 | * frequent operations (opening window/ display surfaces, getting inputs from 33 | * the user, loading libraries...). 34 | * 35 | * SDL is written in C. 36 | * C here is really valuable for the portability of the SDL to many platforms, 37 | * and bindings to other programming languages. However, this makes the library 38 | * rely on direct manipulation of pointers and data structure in memory, and 39 | * manual management of the lifetime of the resources owned by said pointer. 40 | * 41 | * The goal of this project is to provide a modern, easy to use C++ wrapper 42 | * around SDL2. Including it's internal 2D renderer, and as an aid in 43 | * developement using other libraries in a cross-platform context. 44 | * 45 | * These wrappers are implemented only on header files, and it's permissive 46 | * license allows you to simply add it's source code to any project that include 47 | * and links the SDL, and that can be built with a modern C++ compiler following 48 | * the latest standards. 49 | * 50 | * This library mainly contains wrapper around SDL resources that are managed 51 | * using the RAII (Resource Acquisition Is Initialization) idiom. This simply 52 | * means that theses objects will do the dirty work for you by obtaining these 53 | * resources (window, memory...) at their construciton, and clean up when they 54 | * naturally go out of scope, making memory safe and leak-free programs easier 55 | * to accomplish. 56 | * 57 | * This library also allows you to access to all the SDL functionalities that 58 | * are exposed via a collection of functions. 59 | * 60 | * For example, all the "SDL_Functions" that act on a window and take an 61 | * SDL_Window pointer as argument are now simpler to use function members of the 62 | * sdl::Window class. 63 | * 64 | * \section license License 65 | * 66 | * This softwrae is distributed under the terms of the MIT License 67 | * 68 | * Copyright (c) 2018-2019 Edhebi and contributors 69 | * 70 | */ 71 | 72 | namespace sdl 73 | { 74 | ///\brief This class represent the "library entry point". Create and keep an instance of this object before creating any other sdl object (e.g. sdl::Window) 75 | /// 76 | ///Create an sdl::Root object on the stack, and SDL is initialized. 77 | ///When your instance of Root goes out of scope, SDL will be deinitialized for you. 78 | ///This pattern (idiom) is called RAII. If you aren't familiar with it, I suggest reading 79 | ///https://en.cppreference.com/w/cpp/language/raii 80 | 81 | // clang-format off 82 | struct [[nodiscard]] Root 83 | { 84 | ///\brief Construct a root object. This object initialize SDL for you. SDL is de-initialized when object quit scope 85 | ///\param flags The intialization flags that you want to pass to SDL_Init. Default value is SDL_INIT_EVERYTHING 86 | /// 87 | ///This constructor will throw an sdl::Exception if SDL_Init fails. 88 | Root(Uint32 flags = SDL_INIT_EVERYTHING) 89 | { 90 | if (SDL_Init(flags) != 0) 91 | throw Exception{"SDL_Init"}; 92 | } 93 | 94 | ///Automatically quit SDL for you! 95 | ~Root() 96 | { 97 | SDL_Quit(); 98 | } 99 | }; 100 | // clang-format on 101 | 102 | } // namespace sdl 103 | -------------------------------------------------------------------------------- /sources/cpp-sdl2/shared_object.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | #include "exception.hpp" 8 | 9 | namespace sdl 10 | { 11 | ///\brief Represent a shared object (dynamic library, dynamically loaded library, module, plugin...). 12 | /// 13 | ///This class wraps a platform-specific "handle" to a loaded library. 14 | ///An instanciated SharedObject permit to retrieve callable function from them in a cross-platform way. 15 | ///This class automatically manages the lifetime of the loaded library for you 16 | class SharedObject 17 | { 18 | ///Types should be at least named. Alias void* to "SharedObjectHandle* 19 | using SharedObjectHandle = void*; 20 | ///This class wrap an handle to a shared object 21 | SharedObjectHandle handle_ = nullptr; 22 | 23 | public: 24 | ///Alias void* to a descriptive "pointer to function" typename 25 | using FunctionAddress = void*; 26 | 27 | ///Get a shared object ( = Load the named library dynamically) 28 | ///\param objectName Name of the library. 29 | SharedObject(std::string const& objectName) : handle_{SDL_LoadObject(objectName.c_str())} 30 | { 31 | if (!handle_) throw Exception("SDL_LoadObject"); 32 | } 33 | 34 | ///Construct an empty shared object handler. You can move assign to it. 35 | SharedObject() = default; 36 | 37 | ///Automatically unload the library for you 38 | ~SharedObject() { SDL_UnloadObject(handle_); } 39 | 40 | /// Move ctor 41 | SharedObject(SharedObject&& other) noexcept { *this = std::move(other); } 42 | 43 | ///Move shared object into this one 44 | SharedObject& operator=(SharedObject&& other) noexcept 45 | { 46 | if (handle_ != other.handle_) 47 | { 48 | // If we currently hold an object 49 | if (handle_) 50 | { 51 | // Free so we do not leak 52 | SDL_UnloadObject(handle_); 53 | } 54 | 55 | // Assign from other handle, and set it to null 56 | handle_ = other.handle_; 57 | other.handle_ = nullptr; 58 | } 59 | 60 | return *this; 61 | } 62 | 63 | ///This class isn't copyable 64 | SharedObject(SharedObject const&) = delete; 65 | 66 | ///This class isn't copyable 67 | SharedObject& operator=(SharedObject const&) = delete; 68 | 69 | ///Retrieve the raw address of a function inside the owned object. User has to cast this to the correct function pointer type 70 | FunctionAddress function_pointer(std::string const& functionName) const 71 | { 72 | const auto address = SDL_LoadFunction(handle_, functionName.c_str()); 73 | if (!address) throw Exception("SDL_LoadFunction"); 74 | return address; 75 | } 76 | 77 | ///Syntactic sugar overload, provide you a way to specify the actual type of the function pointer 78 | /// e.g: mySharedObject.function_pointer("nameOfExportedFunction"); 79 | ///\param functionName The name of a callable symbol that can be found in the loaded library 80 | template 81 | FunctionPointerSignature function_pointer(std::string const& functionName) const 82 | { 83 | return reinterpret_cast(function_pointer(functionName)); 84 | } 85 | }; 86 | 87 | } // namespace sdl 88 | -------------------------------------------------------------------------------- /sources/cpp-sdl2/simd.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "SDL_version.h" 4 | #if SDL_VERSION_ATLEAST(2, 0, 10) 5 | 6 | #include "SDL_cpuinfo.h" 7 | #include 8 | #include 9 | 10 | namespace sdl::simd 11 | { 12 | /// Report the alignment this system needs for SIMD allocations. 13 | /// See `SDL_SIMDGetAlignment` for more info. 14 | inline size_t get_alignment() 15 | { 16 | return SDL_SIMDGetAlignment(); 17 | } 18 | 19 | /// Allocate memory in a SIMD-friendly way. 20 | /// See `SDL_SIMDAlloc` for more info. 21 | inline void* alloc(size_t len) 22 | { 23 | return SDL_SIMDAlloc(len); 24 | } 25 | 26 | /// Deallocate memory obtained from sdl::simd::alloc(). 27 | inline void free(void* ptr) 28 | { 29 | return SDL_SIMDFree(ptr); 30 | } 31 | 32 | /// Allocator usable with standard containers. 33 | template 34 | struct allocator 35 | { 36 | using value_type = T; 37 | 38 | template 39 | struct rebind 40 | { 41 | using other = allocator; 42 | }; 43 | 44 | constexpr allocator() noexcept = default; 45 | 46 | template 47 | explicit constexpr allocator(allocator) noexcept 48 | { 49 | } 50 | 51 | T* allocate(std::size_t size) 52 | { 53 | void* mem = simd::alloc(size); 54 | #ifndef CPP_SDL2_DISABLE_EXCEPTIONS 55 | if (!mem) throw std::bad_alloc(); 56 | #endif 57 | return std::launder(reinterpret_cast(new (mem) std::byte[size * sizeof(T)])); 58 | } 59 | 60 | void deallocate(T* ptr, [[maybe_unused]] std::size_t size) noexcept { simd::free(ptr); } 61 | 62 | friend constexpr bool operator==(allocator, allocator) noexcept { return true; } 63 | friend constexpr bool operator!=(allocator, allocator) noexcept { return false; } 64 | }; 65 | 66 | namespace details 67 | { 68 | /// recursive implementation of `std::destroy_at`, only available from C++20 onwards. 69 | template 70 | void destroy_at(T* ptr) 71 | { 72 | static_assert(!(std::is_array_v && std::extent_v == 0), "destroy_at is invalid"); 73 | 74 | if constexpr (std::is_array_v) 75 | for (auto& elem : *ptr) details::destroy_at(std::addressof(elem)); 76 | else 77 | ptr->~T(); 78 | } 79 | } // namespace details 80 | 81 | /// deleter usable with `std::unique_ptr`. 82 | template 83 | struct deleter 84 | { 85 | constexpr deleter() noexcept = default; 86 | 87 | template>> 88 | constexpr deleter(deleter) noexcept 89 | { 90 | } 91 | 92 | void operator()(T* ptr) noexcept 93 | { 94 | details::destroy_at(ptr); 95 | simd::free(ptr); 96 | } 97 | }; 98 | 99 | /// deleter usable with `std::unique_ptr`. 100 | template 101 | class deleter 102 | { 103 | std::size_t count = 0; 104 | 105 | public: 106 | deleter() = delete; 107 | 108 | explicit constexpr deleter(std::size_t size) noexcept : count(size) {} 109 | 110 | template>> 111 | explicit constexpr deleter(deleter const& other) noexcept : count(other.count) 112 | { 113 | } 114 | 115 | template 116 | auto operator()(U* ptr) -> std::enable_if_t> 117 | { 118 | for (std::size_t i = 0; i < count; ++i) details::destroy_at(std::addressof((*ptr)[i])); 119 | simd::free(ptr); 120 | } 121 | }; 122 | 123 | template 124 | using unique_ptr = std::unique_ptr>; 125 | 126 | /// Equivalent of `std::make_unique` that returns a simd::unique_ptr. 127 | template 128 | auto make_unique(Args&&... args) -> std::enable_if_t, unique_ptr> 129 | { 130 | allocator a; 131 | return unique_ptr(new (a.allocate(1)) T(std::forward(args)...)); 132 | } 133 | 134 | /// `make_unique` is deleted. 135 | template 136 | auto make_unique(Args&&... args) -> std::enable_if_t != 0, unique_ptr> = delete; 137 | 138 | /// Allocate and default construct `count` elements. 139 | template 140 | auto make_unique(std::size_t count) 141 | -> std::enable_if_t && std::extent_v == 0, unique_ptr> 142 | { 143 | using U = std::remove_extent_t; 144 | allocator a; 145 | auto* mem = a.allocate(count); 146 | for (std::size_t i = 0; i < count; ++i) new (mem + i) U; 147 | return unique_ptr(mem, deleter(count)); 148 | } 149 | 150 | /// Equivalent of `std::make_shared` that uses simd-friendly storage. 151 | template 152 | auto make_shared(Args&&... args) -> std::enable_if, std::shared_ptr> 153 | { 154 | allocator a; 155 | auto* mem = new (a.allocate(1)) T(std::forward(args)...); 156 | return std::shared_ptr(mem, deleter()); 157 | } 158 | 159 | } // namespace sdl::simd 160 | 161 | #endif // SDL_VERSION_ATLEAST(2, 0, 10) 162 | -------------------------------------------------------------------------------- /sources/cpp-sdl2/surface.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | #ifdef CPP_SDL2_ENABLE_SDL_IMAGE 9 | #include 10 | #endif 11 | 12 | #include "color.hpp" 13 | #include "exception.hpp" 14 | #include "pixel.hpp" 15 | #include "rect.hpp" 16 | #include "vec2.hpp" 17 | 18 | namespace sdl 19 | { 20 | ///Represent an SDL_Surface 21 | class Surface 22 | { 23 | public: 24 | ///Represent a lock to safely access surface content 25 | class Lock 26 | { 27 | friend class Surface; 28 | 29 | public: 30 | ///Get pixel at given coordinates inside surface 31 | Pixel at(Vec2i const& pos) const { return at(pos.x, pos.y); } 32 | ///Get pixel at specified location inside surface 33 | Pixel at(size_t x, size_t y) const 34 | { 35 | return Pixel{ 36 | static_cast(surface_->pixels) + (y * surface_->pitch) 37 | + (x * surface_->format->BytesPerPixel), 38 | *surface_->format}; 39 | } 40 | 41 | ///Get pixel at specified location via operator[] 42 | Pixel operator[](Vec2i const& pos) const { return at(pos.x, pos.y); } 43 | 44 | ///Get access to the raw array of pixels 45 | void* raw_array() const { return surface_->pixels; } 46 | 47 | ///Free the lock 48 | ~Lock() { SDL_UnlockSurface(surface_); } 49 | 50 | private: 51 | ///Private constructor for lock object. 52 | constexpr Lock(SDL_Surface& surface_) : surface_{&surface_} {} 53 | 54 | ///Raw pointer to the surface 55 | SDL_Surface* surface_; 56 | }; 57 | 58 | ///Explicit converting ctor from C SDL_Surface to sdl::Surface 59 | explicit Surface(SDL_Surface* surface) : surface_{surface} {} 60 | 61 | Surface(Surface&& other) noexcept { *this = std::move(other); } 62 | 63 | Surface& operator=(Surface&& other) noexcept 64 | { 65 | if (surface_ != other.surface_) 66 | { 67 | SDL_FreeSurface(surface_); 68 | surface_ = other.surface_; 69 | other.surface_ = nullptr; 70 | } 71 | return *this; 72 | } 73 | 74 | ///Create surface for given parameters 75 | Surface( 76 | Uint32 flags, 77 | int w, 78 | int h, 79 | int depth, 80 | Uint32 rmask, 81 | Uint32 gmask, 82 | Uint32 bmask, 83 | Uint32 amask) 84 | : surface_{SDL_CreateRGBSurface(flags, w, h, depth, rmask, gmask, bmask, amask)} 85 | { 86 | if (!surface_) throw Exception{"SDL_CreateRGBSurface"}; 87 | } 88 | 89 | ///Create surface from array of pixels 90 | Surface( 91 | void* pixels, 92 | int w, 93 | int h, 94 | int depth, 95 | int pitch, 96 | Uint32 rmask, 97 | Uint32 gmask, 98 | Uint32 bmask, 99 | Uint32 amask) 100 | : surface_{SDL_CreateRGBSurfaceFrom(pixels, w, h, depth, pitch, rmask, gmask, bmask, amask)} 101 | { 102 | if (!surface_) throw Exception{"SDL_CreateRGBSurfaceFrom"}; 103 | } 104 | 105 | ///Create surface with specified format 106 | Surface(Uint32 flags, int w, int h, int depth, Uint32 format) 107 | : surface_{SDL_CreateRGBSurfaceWithFormat(flags, w, h, depth, format)} 108 | { 109 | if (!surface_) throw Exception{"SDL_CreateRGBSurfaceWithFormat"}; 110 | } 111 | 112 | ///Create surface from array of pixels 113 | Surface(void* pixels, int w, int h, int depth, int pitch, int format) 114 | : surface_{SDL_CreateRGBSurfaceWithFormatFrom(pixels, w, h, depth, pitch, format)} 115 | { 116 | if (!surface_) throw Exception{"SDL_CreateRGBSurfaceWithFormatFrom"}; 117 | } 118 | 119 | #ifdef CPP_SDL2_ENABLE_SDL_IMAGE 120 | ///Construct surface from image file, require SDL_Image 121 | Surface(std::string const& filename) : surface_{IMG_Load(filename.c_str())} 122 | { 123 | if (!surface_) throw Exception{"IMG_Load"}; 124 | } 125 | 126 | #else 127 | ///Stubbed constructor for missing SDL_Image usage. 128 | Surface(std::string const& filename) : surface_{nullptr} 129 | { 130 | SDL_SetError( 131 | "Tried to call sdl::Surface(std::string const& filename) ctor. " 132 | "This function should call IMG_Load() from SDL_Image.\n" 133 | "This program was built without SDL_Image.\n" 134 | "Please Install SDL_Image and #define CPP_SDL2_USE_SDL_IMAGE " 135 | "before including sdl.hpp to use this functionality"); 136 | throw Exception("IMG_Load"); 137 | } 138 | #endif 139 | 140 | ///RAII dtor to automatically free the surface 141 | ~Surface() { SDL_FreeSurface(surface_); } 142 | 143 | ///Get C SDL_Surface object 144 | SDL_Surface* ptr() const { return surface_; } 145 | 146 | ///Convert surface to given format 147 | ///\param format What pixel format to use 148 | Surface with_format(SDL_PixelFormat const& format) const 149 | { 150 | auto s = SDL_ConvertSurface(surface_, &format, 0); 151 | if (!s) throw Exception{"SDL_ConvertSurface"}; 152 | return Surface{s}; 153 | } 154 | 155 | ///convert surface from given format 156 | ///\param format what format to use 157 | Surface with_format(Uint32 format) const 158 | { 159 | auto s = SDL_ConvertSurfaceFormat(surface_, format, 0); 160 | if (!s) throw Exception{"SDL_ConvertSurfaceFormat"}; 161 | return Surface{s}; 162 | } 163 | 164 | ///Convert this surface to specified format 165 | Surface& convert_to(SDL_PixelFormat const& format) { return *this = with_format(format); } 166 | ///Convert this surface to specified format 167 | Surface& convert_to(Uint32 format) { return *this = with_format(format); } 168 | 169 | #if SDL_VERSION_ATLEAST(2, 0, 9) 170 | bool has_colorkey() const { return SDL_HasColorKey(surface_) == SDL_TRUE; } 171 | #endif 172 | 173 | ///Blit surface on another 174 | void blit_on(Rect const& src, Surface& surf, Rect const& dst) const 175 | { 176 | // dst rectangle will *not* be modified by a blit 177 | auto dstmut = const_cast(dst); 178 | if (SDL_BlitSurface(surface_, &src, surf.surface_, &dstmut) != 0) 179 | { 180 | throw Exception{"SDL_BlitSurface"}; 181 | } 182 | } 183 | 184 | ///Blit surface on another 185 | void blit_on(Surface& surf, Rect const& dst) const 186 | { // dst rectangle will *not* be modified by a blit 187 | auto dstmut = const_cast(dst); 188 | if (SDL_BlitSurface(surface_, nullptr, surf.surface_, &dstmut) != 0) 189 | { 190 | throw Exception{"SDL_BlitSurface"}; 191 | } 192 | } 193 | 194 | ///Get width of surface 195 | int width() const { return surface_->w; } 196 | ///Get height of surface 197 | int height() const { return surface_->h; } 198 | ///Get size of surface 199 | Vec2i size() const { return Vec2i{width(), height()}; } 200 | 201 | ///Get surface's pixel format 202 | SDL_PixelFormat const& pixelformat() const { return *surface_->format; } 203 | 204 | ///Get surface format 205 | Uint32 format() const { return surface_->format->format; } 206 | 207 | ///Get surface flags 208 | Uint32 flags() const { return surface_->flags; } 209 | 210 | ///Get surface clip rectangle 211 | Rect cliprect() const 212 | { 213 | Rect r; 214 | SDL_GetClipRect(surface_, &r); 215 | return r; 216 | } 217 | 218 | ///disable colorkey 219 | void disable_colorkey() const 220 | { 221 | if (SDL_SetColorKey(surface_, SDL_FALSE, 0) != 0) throw Exception{"SDL_SetColorKey"}; 222 | } 223 | 224 | ///Set colorkey 225 | void set_colorkey(Uint32 key) const 226 | { 227 | if (SDL_SetColorKey(surface_, SDL_TRUE, key) != 0) throw Exception{"SDL_SetColorKey"}; 228 | } 229 | ///Set colorkey 230 | void set_colorkey(Color const& color) const 231 | { 232 | if (SDL_SetColorKey(surface_, SDL_TRUE, color.as_uint(pixelformat())) != 0) 233 | { 234 | throw Exception{"SDL_SetColorKey"}; 235 | } 236 | } 237 | 238 | ///Get color key 239 | Color colorkey() const 240 | { 241 | Uint32 k; 242 | if (SDL_GetColorKey(surface_, &k) != 0) throw Exception{"SDL_GetColorKey"}; 243 | return Color{k, pixelformat()}; 244 | } 245 | 246 | ///Set surface blend mode 247 | void set_blendmode(SDL_BlendMode const& bm) const 248 | { 249 | if (SDL_SetSurfaceBlendMode(surface_, bm) != 0) throw Exception{"SDL_SetSurfaceBlendMode"}; 250 | } 251 | SDL_BlendMode blendmode() const 252 | { 253 | SDL_BlendMode bm; 254 | if (SDL_GetSurfaceBlendMode(surface_, &bm) != 0) throw Exception{"SDL_GetSurfaceBlendMode"}; 255 | return bm; 256 | } 257 | 258 | ///Set colormod 259 | void set_colormod(Color const& color) const { set_colormod(color.r, color.g, color.b); } 260 | ///Set colormod 261 | void set_colormod(Uint8 r, Uint8 g, Uint8 b) const 262 | { 263 | if (SDL_SetSurfaceColorMod(surface_, r, g, b)) throw Exception{"SDL_SetSurfaceColorMod"}; 264 | } 265 | 266 | ///Get colormod 267 | Color colormod() const 268 | { 269 | Color c; 270 | if (SDL_GetSurfaceColorMod(surface_, &c.r, &c.g, &c.b) != 0) 271 | throw Exception{"SDL_SetSurfaceColorMod"}; 272 | return c; 273 | } 274 | 275 | ///Set alphamod 276 | void set_alphamod(Uint8 alpha) const 277 | { 278 | if (SDL_SetSurfaceAlphaMod(surface_, alpha) != 0) throw Exception{"SDL_SetSurfaceAlphaMod"}; 279 | } 280 | ///Get alphamod 281 | Uint8 alphamod() const 282 | { 283 | Uint8 alpha; 284 | if (SDL_GetSurfaceAlphaMod(surface_, &alpha) != 0) 285 | throw Exception{"SDL_GetSurfaceAlphaMod"}; 286 | return alpha; 287 | } 288 | 289 | ///Set color and alpha mod 290 | void set_coloralphamod(Uint8 r, Uint8 g, Uint8 b, Uint8 a) const 291 | { 292 | set_colormod(r, g, b); 293 | set_alphamod(a); 294 | } 295 | ///Set color and alpha mod 296 | void set_coloralphamod(Color const& c) const 297 | { 298 | set_colormod(c.r, c.g, c.b); 299 | set_alphamod(c.a); 300 | } 301 | ///Get color and alpha mod 302 | Color coloralphamod() const 303 | { 304 | auto c = colormod(); 305 | c.a = alphamod(); 306 | return c; 307 | } 308 | 309 | ///Lock surface for raw access to pixel. Return a lock object that permit 310 | ///to access the pixels. When lock goes out of scope, resources 311 | /// goes unlocked automatically 312 | [[nodiscard]] Lock lock() 313 | { 314 | if (SDL_LockSurface(surface_) != 0) throw Exception{"SDL_LockSurface"}; 315 | return Lock{*surface_}; 316 | } 317 | 318 | private: 319 | ///Set surface pointer 320 | SDL_Surface* surface_ = nullptr; 321 | }; 322 | 323 | } // namespace sdl 324 | -------------------------------------------------------------------------------- /sources/cpp-sdl2/texture.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "color.hpp" 4 | #include "exception.hpp" 5 | #include "pixel.hpp" 6 | #include "rect.hpp" 7 | #include "surface.hpp" 8 | #include "vec2.hpp" 9 | 10 | #include 11 | 12 | #include 13 | 14 | namespace sdl 15 | { 16 | ///Class that represet a renderer texture 17 | class Texture 18 | { 19 | public: 20 | ///Lock object that permit to access pixels directly on texture 21 | class Lock 22 | { 23 | friend class Texture; 24 | 25 | public: 26 | ///Get pixel at given location on texture 27 | Pixel at(Vec2i const& pos) const { return at(pos.x, pos.y); } 28 | ///Get pixel at given location on texture 29 | Pixel at(size_t x, size_t y) const 30 | { 31 | return Pixel{ 32 | static_cast(pixels_) + (y * pitch_) + (x * format_->BytesPerPixel), 33 | *format_}; 34 | } 35 | 36 | ///Get pixel at location with operator[] 37 | Pixel operator[](Vec2i const& pos) const { return at(pos.x, pos.y); } 38 | 39 | ///Automatically unlock texture 40 | ~Lock() 41 | { 42 | SDL_UnlockTexture(texture_); 43 | SDL_FreeFormat(format_); 44 | } 45 | 46 | private: 47 | ///private ctor to create a lock. Lock are created by Texture class 48 | Lock(SDL_Texture* texture, SDL_Rect const* rect) : texture_{texture} 49 | { 50 | if (SDL_LockTexture(texture_, rect, &pixels_, &pitch_) != 0) 51 | { 52 | throw Exception{"SDL_LockTexture"}; 53 | } 54 | 55 | Uint32 f; 56 | SDL_QueryTexture(texture_, &f, nullptr, nullptr, nullptr); 57 | format_ = SDL_AllocFormat(f); 58 | 59 | if (!format_) throw Exception{"SDL_AllocFormat"}; 60 | } 61 | 62 | ///pointer to raw texure 63 | SDL_Texture* texture_; 64 | ///pointer to pixel array 65 | void* pixels_; 66 | ///Pixel pitch 67 | int pitch_; 68 | ///Pixel format 69 | SDL_PixelFormat* format_; 70 | }; 71 | 72 | ///Construct texture from C SDL_Texture object 73 | explicit Texture(SDL_Texture* t) : texture_{t} {}; 74 | 75 | ///Get SDL_Texture pointer from texture 76 | SDL_Texture* ptr() const { return texture_; } 77 | 78 | ///Create texture 79 | Texture(SDL_Renderer* render, Uint32 format, SDL_TextureAccess access, int w, int h) 80 | : Texture{SDL_CreateTexture(render, format, access, w, h)} 81 | { 82 | if (!texture_) throw Exception{"SDL_CreateTexture"}; 83 | } 84 | 85 | ///Default constructor for empty texture object 86 | Texture() = default; 87 | 88 | ///Create texture 89 | Texture(SDL_Renderer* render, Uint32 format, SDL_TextureAccess access, Vec2i size) 90 | : Texture{render, format, access, size.x, size.y} 91 | { 92 | } 93 | 94 | ///Create texture from surface 95 | Texture(SDL_Renderer* render, Surface const& surface) 96 | : Texture{SDL_CreateTextureFromSurface(render, surface.ptr())} 97 | { 98 | if (!texture_) throw Exception{"SDL_CreateTextureFromSurface"}; 99 | } 100 | 101 | ///Create texture from file 102 | Texture(SDL_Renderer* render, std::string const& filename) : Texture{render, Surface{filename}} 103 | { 104 | } 105 | 106 | ///Move texture into this one 107 | Texture(Texture&& other) noexcept { *this = std::move(other); } 108 | 109 | ///Move texture into this one 110 | Texture& operator=(Texture&& other) noexcept 111 | { 112 | if (texture_ != other.texture_) 113 | { 114 | SDL_DestroyTexture(texture_); 115 | texture_ = other.texture_; 116 | other.texture_ = nullptr; 117 | } 118 | 119 | return *this; 120 | } 121 | 122 | ///Non copiable 123 | Texture(Texture const&) = delete; 124 | ///Non copiable 125 | Texture& operator=(Texture const&) = delete; 126 | 127 | ///Destroy texture when it's not in use anymore 128 | ~Texture() { SDL_DestroyTexture(texture_); } 129 | 130 | ///Set texture blend mode 131 | void set_blendmode(SDL_BlendMode const& bm) const 132 | { 133 | if (SDL_SetTextureBlendMode(texture_, bm) != 0) throw Exception{"SDL_SetTextureBlendMode"}; 134 | } 135 | ///Get texture blend mode 136 | SDL_BlendMode blendmode() const 137 | { 138 | SDL_BlendMode bm; 139 | if (SDL_GetTextureBlendMode(texture_, &bm) != 0) throw Exception{"SDL_GetTextureBlendMode"}; 140 | return bm; 141 | } 142 | 143 | ///Set colormod 144 | void set_colormod(Color const& color) const { set_colormod(color.r, color.g, color.b); } 145 | ///Set colormod 146 | void set_colormod(Uint8 r, Uint8 g, Uint8 b) const 147 | { 148 | if (SDL_SetTextureColorMod(texture_, r, g, b) != 0) 149 | throw Exception{"SDL_SetTextureColorMod"}; 150 | } 151 | 152 | ///Get colormod 153 | Color colormod() const 154 | { 155 | Color c; 156 | if (SDL_GetTextureColorMod(texture_, &c.r, &c.g, &c.b) != 0) 157 | throw Exception{"SDL_SetTextureColorMod"}; 158 | return c; 159 | } 160 | 161 | ///Set alphamod 162 | void set_alphamod(Uint8 alpha) const 163 | { 164 | if (SDL_SetTextureAlphaMod(texture_, alpha) != 0) throw Exception{"SDL_SetTextureAlphaMod"}; 165 | } 166 | ///Set alphamod 167 | Uint8 alphamod() const 168 | { 169 | Uint8 alpha; 170 | if (SDL_GetTextureAlphaMod(texture_, &alpha) != 0) 171 | throw Exception{"SDL_GetTextureAlphaMod"}; 172 | return alpha; 173 | } 174 | 175 | ///Set coloralphamod 176 | void set_coloralphamod(Uint8 r, Uint8 g, Uint8 b, Uint8 a) const 177 | { 178 | set_colormod(r, g, b); 179 | set_alphamod(a); 180 | } 181 | ///Set coloralphamod 182 | void set_coloralphamod(Color const& c) const 183 | { 184 | set_colormod(c); 185 | set_alphamod(c.a); 186 | } 187 | ///Get coloralphamod 188 | Color coloralphamod() const 189 | { 190 | auto c = colormod(); 191 | c.a = alphamod(); 192 | return c; 193 | } 194 | 195 | ///Get texture format 196 | Uint32 format() const 197 | { 198 | Uint32 f; 199 | if (SDL_QueryTexture(texture_, &f, nullptr, nullptr, nullptr) != 0) 200 | throw Exception{"SDL_QueryTexture"}; 201 | return f; 202 | } 203 | 204 | ///Access texture 205 | int access() const 206 | { 207 | int a; 208 | if (SDL_QueryTexture(texture_, nullptr, &a, nullptr, nullptr) != 0) 209 | throw Exception{"SDL_QueryTexture"}; 210 | return a; 211 | } 212 | 213 | ///Get texture size 214 | Vec2i size() const 215 | { 216 | Vec2i s; 217 | if (SDL_QueryTexture(texture_, nullptr, nullptr, &s.x, &s.y) != 0) 218 | throw Exception{"SDL_QueryTexture"}; 219 | return s; 220 | } 221 | 222 | ///lock texture for direct access to content 223 | [[nodiscard]] Lock lock() { return Lock{texture_, nullptr}; } 224 | 225 | ///lock texture rect 226 | [[nodiscard]] Lock lock(Rect const& rect) { return Lock{texture_, &rect}; } 227 | 228 | private: 229 | SDL_Texture* texture_ = nullptr; 230 | }; 231 | 232 | } // namespace sdl 233 | -------------------------------------------------------------------------------- /sources/cpp-sdl2/timer.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "exception.hpp" 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | namespace sdl 10 | { 11 | ///Represent an SDL timer 12 | class Timer 13 | { 14 | SDL_TimerID timer_ = 0; 15 | 16 | ///construct 17 | Timer(SDL_TimerID timer) : timer_{timer} {} 18 | 19 | public: 20 | ///A timer object that is not tied to a timer id in SDL doesn't make senes 21 | Timer() = delete; 22 | 23 | ///Just so that the type of a callback function can be 24 | using Callback = SDL_TimerCallback; 25 | 26 | ///Remove the timer, return true if timer was removed 27 | bool remove() 28 | { 29 | if (timer_ > 0) 30 | return SDL_RemoveTimer(timer_); 31 | else 32 | return false; 33 | } 34 | 35 | ///Factory function 36 | static Timer create(uint32_t interval, Callback function, void* user_context) 37 | { 38 | const auto id = SDL_AddTimer(interval, function, user_context); 39 | 40 | if (!id) 41 | { 42 | throw Exception("SDL_AddTimer"); 43 | } 44 | 45 | return {id}; 46 | } 47 | 48 | ///Factory function using std::chrono 49 | static Timer create(std::chrono::milliseconds interval, Callback function, void* user_context) 50 | { 51 | return create(static_cast(interval.count()), function, user_context); 52 | } 53 | 54 | ///Wait for `millisec` milliseconds 55 | static void delay(std::chrono::milliseconds millisec) 56 | { 57 | assert(millisec.count() >= 0); 58 | delay(static_cast(millisec.count())); 59 | } 60 | 61 | ///Wait for `millisec` milliseconds 62 | static void delay(uint32_t millisec) { SDL_Delay(millisec); } 63 | 64 | ///Returns the number of milliseconds elapsed as a unint32_t (standard SDL API) 65 | static uint32_t ticks_u32() { return static_cast(ticks().count()); } 66 | 67 | ///Retruns the number of milliseconds 68 | static std::chrono::milliseconds ticks() { return std::chrono::milliseconds(SDL_GetTicks()); } 69 | 70 | ///Return the performance counter value 71 | static uint64_t perf_counter() { return SDL_GetPerformanceCounter(); } 72 | 73 | ///Return the performace frequency value 74 | static uint64_t perf_frequency() { return SDL_GetPerformanceFrequency(); } 75 | 76 | ///Get the id of this timer 77 | SDL_TimerID timer_id() const { return timer_; } 78 | 79 | operator SDL_TimerID() const { return timer_id(); } 80 | }; 81 | } // namespace sdl 82 | -------------------------------------------------------------------------------- /sources/cpp-sdl2/utils.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "window.hpp" 7 | 8 | namespace sdl 9 | { 10 | ///Get version as string 11 | inline std::string version() 12 | { 13 | // Need to use SDL_GetVersion, not the SDL_VERSION macro. 14 | // SDL_VERSION give you the version of SDL used to build the program 15 | // SDL_GetVersion get the version number from the dynamically linked library 16 | SDL_version v; 17 | SDL_GetVersion(&v); 18 | return std::to_string(int(v.major)) + '.' + std::to_string(int(v.minor)) + '.' 19 | + std::to_string(int(v.patch)); 20 | } 21 | 22 | ///Show a message box, usefull to display error messages 23 | inline void show_message_box(uint32_t flags, std::string const& title, std::string const& message) 24 | { 25 | SDL_ShowSimpleMessageBox(flags, title.c_str(), message.c_str(), nullptr); 26 | } 27 | 28 | ///Show a message box, as a modal child of `parent` 29 | inline void show_message_box( 30 | uint32_t flags, std::string const& title, std::string const& message, sdl::Window const& parent) 31 | { 32 | SDL_ShowSimpleMessageBox(flags, title.c_str(), message.c_str(), parent.ptr()); 33 | } 34 | 35 | ///Get information about the system (os, cpu, ram...) 36 | namespace system 37 | { 38 | // new in 2.0.9 39 | #if SDL_VERSION_ATLEAST(2, 0, 9) 40 | inline bool is_tablet() 41 | { 42 | return SDL_IsTablet(); 43 | } 44 | 45 | inline bool has_AVX512F() 46 | { 47 | return SDL_HasAVX512F(); 48 | } 49 | #endif 50 | 51 | ///Get used platform as a string 52 | inline std::string platform() 53 | { 54 | return SDL_GetPlatform(); 55 | } 56 | 57 | ///Get the size of a cacheline 58 | /// \return size of "L1" cache, in bytes 59 | inline int cpu_cacheline_size() 60 | { 61 | return SDL_GetCPUCacheLineSize(); 62 | } 63 | 64 | ///Get the numbers of CPU in the system 65 | inline int cpu_count() 66 | { 67 | return SDL_GetCPUCount(); 68 | } 69 | 70 | ///Get the amount of ram in the system 71 | ///`\return amount of RAM in MB 72 | inline int system_ram() 73 | { 74 | return SDL_GetSystemRAM(); 75 | } 76 | 77 | ///Returns true if system has AMD 3DNow! support 78 | inline bool has_3DNow() 79 | { 80 | return SDL_Has3DNow(); 81 | } 82 | 83 | ///Return true if CPU has AVX instruction set support 84 | inline bool has_AVX() 85 | { 86 | return SDL_HasAVX(); 87 | } 88 | 89 | ///Return true if CPU has AVX2 instruction set support 90 | inline bool has_AVX2() 91 | { 92 | return SDL_HasAVX2(); 93 | } 94 | 95 | ///Return true if cpu has Apple/IBM/Motorola AltiVec SIMD support 96 | inline bool has_AltiVec() 97 | { 98 | return SDL_HasAltiVec(); 99 | } 100 | 101 | ///Return true if cpu has Intel MMX support 102 | inline bool has_MMX() 103 | { 104 | return SDL_HasMMX(); 105 | } 106 | 107 | ///Return true if current cpu has a TSC register 108 | inline bool has_RDTSC() 109 | { 110 | return SDL_HasRDTSC(); 111 | } 112 | 113 | ///Return true if cpu supports 1st gen SSE 114 | inline bool has_SSE() 115 | { 116 | return SDL_HasSSE(); 117 | } 118 | 119 | ///Return true if cpu supports SSE2 120 | inline bool has_SSE2() 121 | { 122 | return has_SSE2(); 123 | } 124 | 125 | ///Return true if cpu supports SSE3 126 | inline bool has_SSE3() 127 | { 128 | return has_SSE3(); 129 | } 130 | 131 | ///Return true if cpu supports SSE41 132 | inline bool has_SSE41() 133 | { 134 | return has_SSE41(); 135 | } 136 | 137 | ///Return true if cpu supports SSE42 138 | inline bool has_SSE42() 139 | { 140 | return has_SSE42(); 141 | } 142 | } // namespace system 143 | 144 | ///Power related functions 145 | namespace power 146 | { 147 | ///Power states 148 | enum class state 149 | { 150 | unknown = SDL_POWERSTATE_UNKNOWN, 151 | on_battery = SDL_POWERSTATE_ON_BATTERY, 152 | no_battery = SDL_POWERSTATE_NO_BATTERY, 153 | charging = SDL_POWERSTATE_CHARGING, 154 | charged = SDL_POWERSTATE_CHARGED 155 | }; 156 | 157 | ///Get current powerstate. See sdl::power::state enumeration for values 158 | inline state get_state() 159 | { 160 | return state(SDL_GetPowerInfo(nullptr, nullptr)); 161 | } 162 | 163 | ///Get battery remaining time 164 | /// \return Estimated number of seconds untils the battery dies 165 | inline int get_battery_remaining_time() 166 | { 167 | int time = 0; 168 | SDL_GetPowerInfo(&time, nullptr); 169 | return time; 170 | } 171 | 172 | ///Get the current battery charge 173 | /// \return estimated remaining charge in the battery in percents 174 | inline int get_battery_remaining_charge() 175 | { 176 | int charge = 0; 177 | SDL_GetPowerInfo(nullptr, &charge); 178 | return charge; 179 | } 180 | } // namespace power 181 | 182 | ///Clipboard management functions 183 | namespace clipboard 184 | { 185 | ///Returns true if clipboard has text 186 | inline bool has_text() 187 | { 188 | return SDL_HasClipboardText(); 189 | } 190 | 191 | ///Returns the content of the clipboard 192 | inline std::string text() 193 | { 194 | return SDL_GetClipboardText(); 195 | } 196 | 197 | ///Set the clipboard to a specific value 198 | inline int get_text(std::string const& text) 199 | { 200 | return SDL_SetClipboardText(text.c_str()); 201 | } 202 | } // namespace clipboard 203 | } // namespace sdl 204 | -------------------------------------------------------------------------------- /sources/cpp-sdl2/vec2.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | ///Contains implementation details 9 | namespace sdl::details 10 | { 11 | ///base content of a vector 2 12 | template 13 | struct Vec2Base 14 | { 15 | T x, y; 16 | }; 17 | 18 | } // namespace sdl::details 19 | 20 | namespace sdl 21 | { 22 | ///Generic templated 2D vector class 23 | template> 24 | class Vec2 : public Base 25 | { 26 | public: 27 | ///ctor, that will initialize vector to null vector 28 | constexpr Vec2() : Base{0, 0} {} 29 | ///ctor that will initialize it to the current value 30 | constexpr Vec2(T x, T y) : Base{x, y} {} 31 | ///Convert SDL_Point to sdl::Vec2 32 | constexpr Vec2(SDL_Point const& p) : Base{p.x, p.y} {} 33 | 34 | constexpr Vec2(Vec2 const&) noexcept = default; 35 | constexpr Vec2(Vec2&&) noexcept = default; 36 | 37 | ///Convert a vector in polar cordinates to cartesian in the 2D plan 38 | template 39 | static constexpr Vec2 from_polar(A alpha, T radius) 40 | { 41 | return Vec2{radius * std::cos(alpha), radius * std::sin(alpha)}; 42 | } 43 | 44 | Vec2& operator=(Vec2 const&) noexcept = default; 45 | Vec2& operator=(Vec2&&) noexcept = default; 46 | 47 | ///Return the opposite vector 48 | constexpr Vec2 operator-() const { return Vec2{-Base::x, -Base::y}; } 49 | 50 | ///Add vector to this one 51 | Vec2& operator+=(Vec2 const& other) 52 | { 53 | Base::x += other.x; 54 | Base::y += other.y; 55 | return *this; 56 | } 57 | ///Substract vector to this one 58 | Vec2& operator-=(Vec2 const& other) 59 | { 60 | Base::x -= other.x; 61 | Base::y -= other.y; 62 | return *this; 63 | } 64 | ///Multiply vector to this one (x1*x2, y1*y2) 65 | Vec2& operator*=(T value) 66 | { 67 | Base::x *= value; 68 | Base::y *= value; 69 | return *this; 70 | } 71 | ///Divide vector to this one (x1/x2, y1/y2) 72 | Vec2& operator/=(T value) 73 | { 74 | Base::x /= value; 75 | Base::y /= value; 76 | return *this; 77 | } 78 | 79 | ///Add vectors together 80 | constexpr Vec2 operator+(Vec2 const& other) const { return Vec2{Base::x, Base::y} += other; } 81 | ///Substract vectors together 82 | constexpr Vec2 operator-(Vec2 const& other) const { return Vec2{Base::x, Base::y} -= other; } 83 | ///Multiply vectors together 84 | constexpr Vec2 operator*(T value) const { return Vec2{Base::x, Base::y} *= value; } 85 | ///Divide vectors together 86 | constexpr Vec2 operator/(T value) const { return Vec2{Base::x, Base::y} /= value; } 87 | 88 | ///Compare vectors, true if they are the same 89 | constexpr bool operator==(Vec2 const& other) const 90 | { 91 | return (Base::x == other.x && Base::y == other.y); 92 | } 93 | ///Compare vectors, false if they are the same 94 | constexpr bool operator!=(Vec2 const& other) const { return !(*this == other); } 95 | 96 | ///Multiply operator that works on the other side 97 | friend constexpr Vec2 operator*(T lhs, Vec2 const& rhs) { return rhs * lhs; } 98 | 99 | ///Clamp vector inside a box 100 | ///\box rectangle were to clamp vector 101 | Vec2 clamped(SDL_Rect const& box) const 102 | { 103 | auto r = Vec2{Base::x, Base::y}; 104 | r.clamp(box); 105 | return r; 106 | } 107 | 108 | ///\copydoc clamped(SDL_Rect const& box) const 109 | void clamp(SDL_Rect const& box) 110 | { 111 | Base::x = clamp(Base::x, box.x, box.x + box.w); 112 | Base::y = clamp(Base::y, box.y, box.y + box.h); 113 | } 114 | 115 | ///Get lenghts of this vector 116 | T length() const { return std::sqrt(Base::x * Base::x + Base::y * Base::y); } 117 | ///Get squared lenght of this vector 118 | T sqlength() const { return Base::x * Base::x + Base::y * Base::y; } 119 | 120 | ///Return true if this vector is null 121 | bool is_null() const { return Base::x == T(0.0L) || Base::y == T(0.0L); } 122 | 123 | ///Return normalized copy of this vector 124 | Vec2 normalized() const 125 | { 126 | auto r = Vec2{Base::x, Base::y}; 127 | r.normalize(); 128 | return r; 129 | } 130 | 131 | ///Normalize this vector 132 | void normalize() 133 | { 134 | if (is_null()) return; 135 | 136 | const auto l = length(); 137 | Base::x /= l; 138 | Base::y /= l; 139 | } 140 | 141 | ///Convert this vectort to a vector of type 142 | template 143 | explicit operator Vec2() const 144 | { 145 | return Vec2{static_cast(Base::x), static_cast(Base::y)}; 146 | } 147 | 148 | ///Print that vector to stream 149 | friend std::ostream& operator<<(std::ostream& stream, Vec2 const& v) 150 | { 151 | return stream << "(x:" << v.x << ",y:" << v.y << ")"; 152 | } 153 | 154 | private: 155 | ///Actual implementation of clamp function 156 | T clamp(T x, T a, T b) { return std::min(std::max(Base::x, a), b); } 157 | }; 158 | 159 | ///Vector of two integers, that is convertible to/from SDL_Point 160 | using Vec2i = Vec2; 161 | ///Vector of two floats 162 | using Vec2f = Vec2; 163 | ///Vector of two double 164 | using Vec2d = Vec2; 165 | 166 | } // namespace sdl 167 | -------------------------------------------------------------------------------- /sources/cpp-sdl2/window.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "renderer.hpp" 8 | #include "vec2.hpp" 9 | 10 | #ifdef CPP_SDL2_ENABLE_VULKAN 11 | #include 12 | #include 13 | #endif 14 | 15 | namespace sdl 16 | { 17 | ///\brief Represent an SDL window. Also contains accessor to any window 18 | /// related adjacent functionality like OpenGL/Vulkan helper functions 19 | class Window 20 | { 21 | public: 22 | ///Construct a window. This safely create an SDL_Window for you 23 | ///\param title Name of the window 24 | ///\param size Size of the window on screen when shown 25 | ///\param flags Any flags needed to be passed to SDL_CreateWindow 26 | Window(std::string const& title, Vec2i const& size, Uint32 flags = SDL_WINDOW_SHOWN) 27 | : window_{SDL_CreateWindow( 28 | title.c_str(), SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, size.x, size.y, flags)} 29 | { 30 | if (!window_) throw Exception{"SDL_CreateWindow"}; 31 | } 32 | 33 | ///Construct an invalid window object. You will need to initialize it by moving in a real sdl::Window in 34 | Window() = default; 35 | 36 | ///Default move ctor 37 | Window(Window&& other) noexcept { *this = std::move(other); } 38 | 39 | ///Move assign operator. If this object represent a valid window, it will be destroyed before 40 | ///acquiring the window_ pointer from other 41 | ///\param other Another sdl::Window object 42 | Window& operator=(Window&& other) noexcept 43 | { 44 | if (this->window_ != other.window_) 45 | { 46 | SDL_DestroyWindow(window_); 47 | window_ = other.window_; 48 | other.window_ = nullptr; 49 | } 50 | return *this; 51 | } 52 | 53 | // Non copyable class 54 | Window(Window&) = delete; 55 | Window& operator=(Window&) = delete; 56 | 57 | ///Destructor. Calls SDL_DestroyWindow() automatically for you. 58 | virtual ~Window() { SDL_DestroyWindow(window_); } 59 | 60 | ///Getter for the raw SDL2 window pointer 61 | SDL_Window* ptr() const { return window_; } 62 | 63 | ///2D renderer factory. Permit to easily create a 2D renderer. 64 | /// Hardware acceleration enabled by default 65 | ///\param flags Any flags needed to be passed to SDL_CreateRenderer. 66 | Renderer make_renderer(Uint32 flags = SDL_RENDERER_ACCELERATED) const 67 | { 68 | const auto render = SDL_CreateRenderer(window_, -1, flags); 69 | if (!render) throw Exception{"SDL_CreateRenderer"}; 70 | return Renderer{render}; 71 | } 72 | 73 | ///Get the current window display index 74 | int display_index() const 75 | { 76 | const auto r = SDL_GetWindowDisplayIndex(window_); 77 | if (r == -1) throw Exception{"SDL_GetWindowDisplayIndex"}; 78 | return r; 79 | } 80 | 81 | ///Set the window display mode 82 | ///\param mode Display mode to use 83 | void set_display_mode(SDL_DisplayMode const& mode) const 84 | { 85 | if (SDL_SetWindowDisplayMode(window_, &mode) != 0) 86 | { 87 | throw Exception{"SDL_SetWindowDisplayMode"}; 88 | } 89 | } 90 | 91 | ///Get the current display mode 92 | SDL_DisplayMode display_mode() const 93 | { 94 | SDL_DisplayMode mode; 95 | if (SDL_GetWindowDisplayMode(window_, &mode) != 0) 96 | { 97 | throw Exception{"SDL_GetWindowDisplayMode"}; 98 | } 99 | return mode; 100 | } 101 | 102 | ///Get the flags of this window 103 | Uint32 flags() const { return SDL_GetWindowFlags(window_); } 104 | 105 | ///Grab window 106 | Window& grab(bool g = true) 107 | { 108 | SDL_SetWindowGrab(window_, static_cast(g)); 109 | return *this; 110 | } 111 | 112 | ///Release window 113 | Window& release(bool r = true) { return grab(!r); } 114 | 115 | ///Is window grabed 116 | bool grabbed() const { return SDL_GetWindowGrab(window_); } 117 | 118 | ///Move window to specific location on screen 119 | ///\param v Vector pointing to the new window location 120 | Window& move_to(Vec2i const& v) 121 | { 122 | SDL_SetWindowPosition(window_, v.x, v.y); 123 | return *this; 124 | } 125 | ///Translate window on screen 126 | ///\param v translation vector 127 | Window& move_by(Vec2i const& v) { return move_to(position() + v); } 128 | ///Get current window position 129 | Vec2i position() const 130 | { 131 | Vec2i pos; 132 | SDL_GetWindowPosition(window_, &pos.x, &pos.y); 133 | return pos; 134 | } 135 | 136 | ///Change the size of the window 137 | ///\newsize the size of the window 138 | void resize(Vec2i const& newsize) const { SDL_SetWindowSize(window_, newsize.x, newsize.y); } 139 | ///Get current window size 140 | Vec2i size() const 141 | { 142 | Vec2i s; 143 | SDL_GetWindowSize(window_, &s.x, &s.y); 144 | return s; 145 | } 146 | 147 | ///Change window name 148 | ///\param t new window "title" 149 | Window& rename(std::string const& t) 150 | { 151 | SDL_SetWindowTitle(window_, t.c_str()); 152 | return *this; 153 | } 154 | ///\Get the current window title 155 | std::string title() const { return std::string{SDL_GetWindowTitle(window_)}; } 156 | 157 | ///Set the window icon 158 | ///\param icon Surface containing the icon to use 159 | void set_icon(Surface const& icon) const { SDL_SetWindowIcon(window_, icon.ptr()); } 160 | 161 | ///Set the window icon (may require linking and activating SDL_Image) 162 | ///\param filename path to a file you can use to set the window icon 163 | void set_icon(std::string const& filename) const 164 | { 165 | auto icon = Surface{filename}; 166 | SDL_SetWindowIcon(window_, icon.ptr()); 167 | } 168 | 169 | ///Hide the window 170 | Window& hide() 171 | { 172 | SDL_HideWindow(window_); 173 | return *this; 174 | } 175 | ///Maximize the window 176 | Window& maximize() 177 | { 178 | SDL_MaximizeWindow(window_); 179 | return *this; 180 | } 181 | ///Minimize the window 182 | Window& minimize() 183 | { 184 | SDL_MinimizeWindow(window_); 185 | return *this; 186 | } 187 | ///Raise the window 188 | Window& raise() 189 | { 190 | SDL_RaiseWindow(window_); 191 | return *this; 192 | } 193 | ///Restore the window 194 | Window& restore() 195 | { 196 | SDL_RestoreWindow(window_); 197 | return *this; 198 | } 199 | 200 | ///Returns true if window is currently fullscreen 201 | /// (both real and "desktop mode") 202 | bool fullscreen() const 203 | { 204 | return flags() & (SDL_WINDOW_FULLSCREEN | SDL_WINDOW_FULLSCREEN_DESKTOP); 205 | } 206 | ///Set the window fullscreen 207 | Window& set_fullscreen(bool fs) 208 | { 209 | if (SDL_SetWindowFullscreen(window_, fs ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0) != 0) 210 | { 211 | throw Exception{"SDL_SetWindowFullscreen"}; 212 | } 213 | return *this; 214 | } 215 | 216 | ///Toggle the window fullscreen 217 | Window& toggle_fullscreen() { return set_fullscreen(!fullscreen()); } 218 | 219 | ///Get window manager info. Exact content of this structure is fully platform dependant. 220 | SDL_SysWMinfo wm_info() const 221 | { 222 | SDL_SysWMinfo info; 223 | SDL_VERSION(&info.version); 224 | if (SDL_GetWindowWMInfo(window_, &info) == SDL_FALSE) 225 | { 226 | throw Exception("SDL_GetWindowWMInfo"); 227 | } 228 | return info; 229 | } 230 | 231 | #ifdef CPP_SDL2_ENABLE_VULKAN 232 | ///Enumerate the required extensions to create a VkSurfaceKHR on the current system 233 | /// \return a vector of const char strings containing the extensions names 234 | std::vector vk_get_instance_extensions() 235 | { 236 | std::vector extensions; 237 | Uint32 count; 238 | 239 | if (!SDL_Vulkan_GetInstanceExtensions(window_, &count, nullptr)) 240 | throw Exception("SDL_Vulkan_GetInstanceExtensions"); 241 | 242 | extensions.resize(count); 243 | 244 | if (!SDL_Vulkan_GetInstanceExtensions(window_, &count, extensions.data())) 245 | throw Exception("SDL_Vulkan_GetInstaceExtensions"); 246 | 247 | return extensions; // Benefit from enforced RVO 248 | } 249 | 250 | ///Create a vulkan surface for the current platform 251 | /// \return your vulkan surface 252 | VkSurfaceKHR vk_create_surface(VkInstance instance) 253 | { 254 | VkSurfaceKHR surface; 255 | if (!SDL_Vulkan_CreateSurface(window_, instance, &surface)) 256 | throw Exception("SDL_Vulkan_CreateSurface"); 257 | 258 | return surface; 259 | } 260 | 261 | ///Create a vulkan surface using the C++ wrapper around UniqueHandle 262 | vk::UniqueSurfaceKHR vk_create_unique_surface(vk::Instance instance) 263 | { 264 | auto nakedSurface = vk_create_surface(instance); 265 | return vk::UniqueSurfaceKHR(nakedSurface, instance); 266 | } 267 | 268 | ///Get the underlayoing drawable size of the window. Use this for vulkan viewport size, scissor rects and any place you need `VkExtent`s 269 | /// \return Size in an integer 2D vector 270 | Vec2i vk_get_drawable_size() 271 | { 272 | Vec2i size; 273 | SDL_Vulkan_GetDrawableSize(window_, &size.x, &size.y); 274 | return size; 275 | } 276 | #endif // vulkan methods 277 | 278 | #ifdef CPP_SDL2_ENABLE_OPENGL 279 | 280 | // This function is mostly used to set values regarding SDL GL context, and 281 | // is intertwined with window creation. However, this can be called before 282 | // creation the window. This wrapping is mostly for API consistency and for 283 | // automatic error checking. 284 | static void gl_set_attribute(SDL_GLattr attr, int val) 285 | { 286 | if (SDL_GL_SetAttribute(attr, val) < 0) throw Exception("SDL_GL_SetAttribute"); 287 | } 288 | 289 | static void gl_set_attribute(SDL_GLattr attr, bool val) { gl_set_attribute(attr, val ? 1 : 0); } 290 | 291 | ///Get the value of the specified OpenGL attribute 292 | static int gl_get_attribute(SDL_GLattr attr) 293 | { 294 | int value; 295 | if (SDL_GL_GetAttribute(attr, &value) != 0) throw Exception("SDL_GL_GetAttribute"); 296 | return value; 297 | } 298 | 299 | ///Reset all OpenGL attributes to their default values 300 | static void gl_reset_attribute() { SDL_GL_ResetAttributes(); } 301 | 302 | ///Return true if the specified extension is supported 303 | static bool gl_is_extension_supported(std::string const& ext_name) 304 | { 305 | return SDL_GL_ExtensionSupported(ext_name.c_str()) == SDL_TRUE; 306 | } 307 | 308 | ///Define the type of window swapping strategy for opengl windows. 309 | enum class gl_swap_interval 310 | { 311 | immediate, 312 | vsync, 313 | adaptive_vsync 314 | }; 315 | 316 | ///Set the swap interval. If exception thrown while attempting to use adaptive vsync, use standard vsync. 317 | static void gl_set_swap_interval(gl_swap_interval swap_mode) 318 | { 319 | auto result = 0; 320 | switch (swap_mode) 321 | { 322 | case gl_swap_interval::immediate: result = SDL_GL_SetSwapInterval(0); break; 323 | case gl_swap_interval::vsync: result = SDL_GL_SetSwapInterval(1); break; 324 | case gl_swap_interval::adaptive_vsync: result = SDL_GL_SetSwapInterval(-1); break; 325 | } 326 | 327 | if (result != 0) throw Exception("SDL_GL_SetSwapInterval"); 328 | } 329 | 330 | ///Get the current swap interval 331 | static gl_swap_interval gl_get_swap_interval() 332 | { 333 | const auto value = SDL_GL_GetSwapInterval(); 334 | if (value == 1) return gl_swap_interval::vsync; 335 | if (value == -1) return gl_swap_interval::adaptive_vsync; 336 | return gl_swap_interval::immediate; 337 | } 338 | 339 | ///Nested class that represent a managed OpenGL Context by the SDL 340 | class GlContext 341 | { 342 | public: 343 | ///Create a GlContext for the given window. 344 | /// You should use sdl::Window::gl_create_context() instead of this 345 | GlContext(SDL_Window* w) : context_{SDL_GL_CreateContext(w)}, owner_{w} 346 | { 347 | if (!context_) throw Exception("SDL_GL_CreateContext"); 348 | } 349 | 350 | ///Construct an invalid GLContext object where you will need to move a new one 351 | GlContext() = default; 352 | 353 | ///Dtor will call SDL_GL_DeleteContext on the enclosed context 354 | ~GlContext() { SDL_GL_DeleteContext(context_); } 355 | 356 | // Only movable, not copyable 357 | GlContext(GlContext const&) = delete; 358 | GlContext& operator=(GlContext const&) = delete; 359 | 360 | GlContext(GlContext&& other) noexcept { *this = std::move(other); } 361 | 362 | GlContext& operator=(GlContext&& other) noexcept 363 | { 364 | if (context_ != other.context_) 365 | { 366 | context_ = other.context_; 367 | owner_ = other.owner_; 368 | other.context_ = nullptr; 369 | } 370 | return *this; 371 | } 372 | 373 | void make_current() const 374 | { 375 | if (SDL_GL_MakeCurrent(owner_, context_) < 0) throw Exception("SDL_GL_MakeCurrent"); 376 | } 377 | 378 | SDL_GLContext ptr() const { return context_; } 379 | 380 | private: 381 | SDL_GLContext context_ = nullptr; 382 | SDL_Window* owner_ = nullptr; 383 | }; 384 | 385 | ///Create an OpenGL context from the current window 386 | GlContext create_context() const { return GlContext{window_}; } 387 | 388 | ///Swap buffers for GL when using double buffering on the current window 389 | void gl_swap() const { SDL_GL_SwapWindow(window_); } 390 | 391 | ///Get the actual size of the OpenGL drawing area on the window. May not match window size on platform that uses display scaling. 392 | Vec2i gl_get_drawable_size() const 393 | { 394 | Vec2i size{}; 395 | SDL_GL_GetDrawableSize(window_, &size.x, &size.y); 396 | return size; 397 | } 398 | 399 | #endif 400 | 401 | private: 402 | ///Raw naked pointer to an SDL window 403 | SDL_Window* window_ = nullptr; 404 | }; 405 | 406 | } // namespace sdl 407 | --------------------------------------------------------------------------------