diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 317d34826..013c69f1f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -32,6 +32,7 @@ jobs: - { name: Windows VS2019 x64, os: windows-2019, flags: -DSFML_USE_MESA3D=ON -GNinja } - { name: Windows VS2022 x86, os: windows-2022, flags: -DSFML_USE_MESA3D=ON -GNinja } - { name: Windows VS2022 x64, os: windows-2022, flags: -DSFML_USE_MESA3D=ON -GNinja } + - { name: Windows VS2022 arm64, os: windows-2022, flags: -DSFML_USE_MESA3D=OFF -GNinja -DSFML_BUILD_TEST_SUITE=OFF } - { name: Windows VS2022 ClangCL MSBuild, os: windows-2022, flags: -DSFML_USE_MESA3D=ON -T ClangCL } # ninja doesn't support specifying the toolset, so use the ClangCL toolset to test building with MSBuild as well - { name: Windows VS2022 OpenGL ES, os: windows-2022, flags: -DSFML_USE_MESA3D=ON -DSFML_OPENGL_ES=ON -GNinja } - { name: Windows VS2022 Unity, os: windows-2022, flags: -DSFML_USE_MESA3D=ON -DCMAKE_UNITY_BUILD=ON -GNinja } @@ -117,7 +118,7 @@ jobs: if: contains(matrix.platform.name, 'Windows VS') && !contains(matrix.platform.name, 'MSBuild') uses: ilammy/msvc-dev-cmd@v1 with: - arch: ${{ contains(matrix.platform.name, 'x86') && 'x86' || 'x64' }} + arch: ${{ contains(matrix.platform.name, 'arm64') && 'amd64_arm64' || contains(matrix.platform.name, 'x86') && 'x86' || 'x64' }} # Although the CMake configuration will run with 3.24 on Windows and 3.22 # elsewhere, we install 3.25 on Windows in order to support specifying @@ -237,7 +238,7 @@ jobs: find build/bin -name test-sfml-window -or -name test-sfml-window.exe -exec sh -c "{} *sf::Context* --section=\"Version String\" --success | grep OpenGL" \; - name: Test (Windows) - if: runner.os == 'Windows' && !contains(matrix.platform.name, 'MinGW') + if: runner.os == 'Windows' && !contains(matrix.platform.name, 'MinGW') && !contains(matrix.platform.name, 'arm64') run: cmake --build build --target runtests --config ${{ matrix.type.name == 'Debug' && 'Debug' || 'Release' }} - name: Test (Linux/macOS/MinGW) @@ -291,7 +292,7 @@ jobs: ctest --test-dir build --output-on-failure -C ${{ matrix.type.name == 'Debug' && 'Debug' || 'Release' }} --repeat until-pass:3 - name: Upload Coverage Report to Coveralls - if: matrix.type.name == 'Debug' && github.repository == 'SFML/SFML' && !contains(matrix.platform.name, 'iOS') && !contains(matrix.platform.name, 'Android') # Disable upload in forks + if: matrix.type.name == 'Debug' && github.repository == 'SFML/SFML' && !contains(matrix.platform.name, 'iOS') && !contains(matrix.platform.name, 'Android') && !contains(matrix.platform.name, 'arm64') # Disable upload in forks uses: coverallsapp/github-action@v2 with: file: ./build/coverage.out diff --git a/cmake/Config.cmake b/cmake/Config.cmake index 35dc26ede..7e5e0c795 100644 --- a/cmake/Config.cmake +++ b/cmake/Config.cmake @@ -6,9 +6,7 @@ if(${CMAKE_SYSTEM_NAME} STREQUAL "Windows") set(OPENGL_ES 0) # detect the architecture - if(${CMAKE_GENERATOR_PLATFORM} MATCHES "ARM64") - set(ARCH_ARM64 1) - elseif("${CMAKE_GENERATOR_PLATFORM}" STREQUAL "" AND ${CMAKE_SYSTEM_PROCESSOR} MATCHES "ARM64") + if("${CMAKE_GENERATOR_PLATFORM}" MATCHES "ARM64" OR "${MSVC_CXX_ARCHITECTURE_ID}" MATCHES "ARM64" OR "${CMAKE_SYSTEM_PROCESSOR}" MATCHES "ARM64") set(ARCH_ARM64 1) elseif(CMAKE_SIZEOF_VOID_P EQUAL 4) set(ARCH_X86 1) diff --git a/cmake/Mesa3D.cmake b/cmake/Mesa3D.cmake index e5970fa75..2889fc64f 100644 --- a/cmake/Mesa3D.cmake +++ b/cmake/Mesa3D.cmake @@ -4,11 +4,11 @@ set(MESA3D_SHA256 "FEF8A643689414A70347AE8027D24674DEFD85E8D6428C8A9D4145BB3F44A get_filename_component(MESA3D_ARCHIVE "${MESA3D_URL}" NAME) get_filename_component(MESA3D_ARCHIVE_DIRECTORY "${MESA3D_URL}" NAME_WLE) -if(${ARCH_X64}) +if(ARCH_X64) set(MESA3D_ARCH "x64") -elseif(${ARCH_X86}) +elseif(ARCH_X86) set(MESA3D_ARCH "x86") -else() +elseif(SFML_USE_MESA3D) message(FATAL_ERROR "Mesa 3D does currently not support the target architecture.") endif() @@ -61,7 +61,7 @@ if(SFML_OS_WINDOWS AND SFML_USE_MESA3D) add_custom_target(install-mesa3d DEPENDS ${MESA3D_INSTALLED_FILES}) set_target_properties(install-mesa3d PROPERTIES EXCLUDE_FROM_ALL ON) -elseif(SFML_OS_WINDOWS AND EXISTS "${MESA3D_ARCH_PATH}") +elseif(SFML_OS_WINDOWS AND MESA3D_ARCH AND EXISTS "${MESA3D_ARCH_PATH}") # we are removing the files # compile a list of file names that we have to remove diff --git a/examples/island/Island.cpp b/examples/island/Island.cpp index 8a356a5f6..0f4548121 100644 --- a/examples/island/Island.cpp +++ b/examples/island/Island.cpp @@ -24,12 +24,10 @@ namespace { // Width and height of the application window -const unsigned int windowWidth = 800; -const unsigned int windowHeight = 600; +const sf::Vector2u windowSize(800, 600); // Resolution of the generated terrain -const unsigned int resolutionX = 800; -const unsigned int resolutionY = 600; +const sf::Vector2u resolution(800, 600); // Thread pool parameters const unsigned int threadCount = 4; @@ -68,7 +66,7 @@ float edgeDropoffExponent = 1.5f; float snowcapHeight = 0.6f; // Terrain lighting parameters -float heightFactor = windowHeight / 2.0f; +float heightFactor = static_cast(windowSize.y) / 2.0f; float heightFlatten = 3.0f; float lightFactor = 0.7f; } // namespace @@ -88,7 +86,7 @@ void generateTerrain(sf::Vertex* vertexBuffer); int main() { // Create the window of the application - sf::RenderWindow window(sf::VideoMode({windowWidth, windowHeight}), "SFML Island", sf::Style::Titlebar | sf::Style::Close); + sf::RenderWindow window(sf::VideoMode(windowSize), "SFML Island", sf::Style::Titlebar | sf::Style::Close); window.setVerticalSyncEnabled(true); const sf::Font font("resources/tuffy.ttf"); @@ -133,14 +131,14 @@ int main() } // Create our VertexBuffer with enough space to hold all the terrain geometry - if (!terrain.create(resolutionX * resolutionY * 6)) + if (!terrain.create(resolution.x * resolution.y * 6)) { std::cerr << "Failed to create vertex buffer" << std::endl; return EXIT_FAILURE; } // Resize the staging buffer to be able to hold all the terrain geometry - terrainStagingBuffer.resize(resolutionX * resolutionY * 6); + terrainStagingBuffer.resize(resolution.x * resolution.y * 6); // Generate the initial terrain generateTerrain(terrainStagingBuffer.data()); @@ -152,7 +150,7 @@ int main() } // Center the status text - statusText.setPosition((sf::Vector2f(windowWidth, windowHeight) - statusText.getLocalBounds().size) / 2.f); + statusText.setPosition((sf::Vector2f(windowSize) - statusText.getLocalBounds().size) / 2.f); // Set up an array of pointers to our settings for arrow navigation constexpr std::array settings = { @@ -279,58 +277,45 @@ int main() /// Get the terrain elevation at the given coordinates. /// //////////////////////////////////////////////////////////// -float getElevation(float x, float y) +float getElevation(sf::Vector2u position) { - x = x / resolutionX - 0.5f; - y = y / resolutionY - 0.5f; + const sf::Vector2f normalized = sf::Vector2f(position).componentWiseDiv(sf::Vector2f(resolution)) - + sf::Vector2f(0.5f, 0.5f); float elevation = 0.0f; for (int i = 0; i < perlinOctaves; ++i) { - elevation += stb_perlin_noise3(x * perlinFrequency * static_cast(std::pow(perlinFrequencyBase, i)), - y * perlinFrequency * static_cast(std::pow(perlinFrequencyBase, i)), - 0, - 0, - 0, - 0) * + const sf::Vector2f scaled = normalized * perlinFrequency * static_cast(std::pow(perlinFrequencyBase, i)); + elevation += stb_perlin_noise3(scaled.x, scaled.y, 0, 0, 0, 0) * static_cast(std::pow(perlinFrequencyBase, -i)); } elevation = (elevation + 1.f) / 2.f; - const float distance = 2.0f * std::sqrt(x * x + y * y); + const float distance = 2.0f * normalized.length(); elevation = (elevation + heightBase) * (1.0f - edgeFactor * std::pow(distance, edgeDropoffExponent)); elevation = std::clamp(elevation, 0.0f, 1.0f); return elevation; } -float getElevation(unsigned int x, unsigned int y) -{ - return getElevation(static_cast(x), static_cast(y)); -} - //////////////////////////////////////////////////////////// /// Get the terrain moisture at the given coordinates. /// //////////////////////////////////////////////////////////// -float getMoisture(float x, float y) +float getMoisture(sf::Vector2u position) { - x = x / resolutionX - 0.5f; - y = y / resolutionY - 0.5f; + const sf::Vector2f normalized = sf::Vector2f(position).componentWiseDiv(sf::Vector2f(resolution)) - + sf::Vector2f(0.5f, 0.5f); + const sf::Vector2f transformed = normalized * 4.f + sf::Vector2f(0.5f, 0.5f); - const float moisture = stb_perlin_noise3(x * 4.f + 0.5f, y * 4.f + 0.5f, 0, 0, 0, 0); + const float moisture = stb_perlin_noise3(transformed.x, transformed.y, 0, 0, 0, 0); return (moisture + 1.f) / 2.f; } -float getMoisture(unsigned int x, unsigned int y) -{ - return getMoisture(static_cast(x), static_cast(y)); -} - //////////////////////////////////////////////////////////// /// Get the lowlands terrain color for the given moisture. @@ -372,18 +357,16 @@ sf::Color getHighlandsTerrainColor(float elevation, float moisture) { const sf::Color lowlandsColor = getLowlandsTerrainColor(moisture); - sf::Color color = moisture < 0.6f ? sf::Color(112, 128, 144) - : colorFromFloats(112 + (110 * (moisture - 0.6f) / 0.4f), - 128 + (56 * (moisture - 0.6f) / 0.4f), - 144 - (9 * (moisture - 0.6f) / 0.4f)); + const sf::Color color = moisture < 0.6f ? sf::Color(112, 128, 144) + : colorFromFloats(112 + (110 * (moisture - 0.6f) / 0.4f), + 128 + (56 * (moisture - 0.6f) / 0.4f), + 144 - (9 * (moisture - 0.6f) / 0.4f)); const float factor = std::min((elevation - 0.4f) / 0.1f, 1.f); - color.r = static_cast(lowlandsColor.r * (1.f - factor) + color.r * factor); - color.g = static_cast(lowlandsColor.g * (1.f - factor) + color.g * factor); - color.b = static_cast(lowlandsColor.b * (1.f - factor) + color.b * factor); - - return color; + return colorFromFloats(lowlandsColor.r * (1.f - factor) + color.r * factor, + lowlandsColor.g * (1.f - factor) + color.g * factor, + lowlandsColor.b * (1.f - factor) + color.b * factor); } @@ -396,15 +379,13 @@ sf::Color getSnowcapTerrainColor(float elevation, float moisture) { const sf::Color highlandsColor = getHighlandsTerrainColor(elevation, moisture); - sf::Color color = sf::Color::White; + const sf::Color color = sf::Color::White; const float factor = std::min((elevation - snowcapHeight) / 0.05f, 1.f); - color.r = static_cast(highlandsColor.r * (1.f - factor) + color.r * factor); - color.g = static_cast(highlandsColor.g * (1.f - factor) + color.g * factor); - color.b = static_cast(highlandsColor.b * (1.f - factor) + color.b * factor); - - return color; + return colorFromFloats(highlandsColor.r * (1.f - factor) + color.r * factor, + highlandsColor.g * (1.f - factor) + color.g * factor, + highlandsColor.b * (1.f - factor) + color.b * factor); } @@ -446,9 +427,7 @@ sf::Vector2f computeNormal(float left, float right, float bottom, float top) const sf::Vector3f deltaX(1, 0, (std::pow(right, heightFlatten) - std::pow(left, heightFlatten)) * heightFactor); const sf::Vector3f deltaY(0, 1, (std::pow(top, heightFlatten) - std::pow(bottom, heightFlatten)) * heightFactor); - sf::Vector3f crossProduct(deltaX.y * deltaY.z - deltaX.z * deltaY.y, - deltaX.z * deltaY.x - deltaX.x * deltaY.z, - deltaX.x * deltaY.y - deltaX.y * deltaY.x); + sf::Vector3f crossProduct = deltaX.cross(deltaY); // Scale cross product to make z component 1.0f so we can drop it crossProduct /= crossProduct.z; @@ -458,6 +437,26 @@ sf::Vector2f computeNormal(float left, float right, float bottom, float top) } +//////////////////////////////////////////////////////////// +/// Compute the vertex representing the terrain at the given +/// coordinates. +/// +//////////////////////////////////////////////////////////// +const auto scalingFactors = sf::Vector2f(windowSize).componentWiseDiv(sf::Vector2f(resolution)); + +sf::Vertex computeVertex(sf::Vector2u position) +{ + sf::Vertex vertex; + vertex.position = sf::Vector2f(position).componentWiseMul(scalingFactors); + vertex.color = getTerrainColor(getElevation(position), getMoisture(position)); + vertex.texCoords = computeNormal(getElevation(position - sf::Vector2u(1, 0)), + getElevation(position + sf::Vector2u(1, 0)), + getElevation(position + sf::Vector2u(0, 1)), + getElevation(position - sf::Vector2u(0, 1))); + return vertex; +}; + + //////////////////////////////////////////////////////////// /// Process a terrain generation work item. Use the vector /// of vertices as scratch memory and upload the data to @@ -466,23 +465,20 @@ sf::Vector2f computeNormal(float left, float right, float bottom, float top) //////////////////////////////////////////////////////////// void processWorkItem(std::vector& vertices, const WorkItem& workItem) { - const unsigned int rowBlockSize = (resolutionY / blockCount) + 1; + const unsigned int rowBlockSize = (resolution.y / blockCount) + 1; const unsigned int rowStart = rowBlockSize * workItem.index; - if (rowStart >= resolutionY) + if (rowStart >= resolution.y) return; - const unsigned int rowEnd = std::min(rowStart + rowBlockSize, resolutionY); + const unsigned int rowEnd = std::min(rowStart + rowBlockSize, resolution.y); const unsigned int rowCount = rowEnd - rowStart; - const float scalingFactorX = float{windowWidth} / float{resolutionX}; - const float scalingFactorY = float{windowHeight} / float{resolutionY}; - for (unsigned int y = rowStart; y < rowEnd; ++y) { - for (unsigned int x = 0; x < resolutionX; ++x) + for (unsigned int x = 0; x < resolution.x; ++x) { - const unsigned int arrayIndexBase = ((y - rowStart) * resolutionX + x) * 6; + const unsigned int arrayIndexBase = ((y - rowStart) * resolution.x + x) * 6; // Top left corner (first triangle) if (x > 0) @@ -491,17 +487,11 @@ void processWorkItem(std::vector& vertices, const WorkItem& workItem } else if (y > rowStart) { - vertices[arrayIndexBase + 0] = vertices[arrayIndexBase - resolutionX * 6 + 1]; + vertices[arrayIndexBase + 0] = vertices[arrayIndexBase - resolution.x * 6 + 1]; } else { - vertices[arrayIndexBase + 0].position = sf::Vector2f(static_cast(x) * scalingFactorX, - static_cast(y) * scalingFactorY); - vertices[arrayIndexBase + 0].color = getTerrainColor(getElevation(x, y), getMoisture(x, y)); - vertices[arrayIndexBase + 0].texCoords = computeNormal(getElevation(x - 1, y), - getElevation(x + 1, y), - getElevation(x, y + 1), - getElevation(x, y - 1)); + vertices[arrayIndexBase + 0] = computeVertex({x, y}); } // Bottom left corner (first triangle) @@ -511,23 +501,11 @@ void processWorkItem(std::vector& vertices, const WorkItem& workItem } else { - vertices[arrayIndexBase + 1].position = sf::Vector2f(static_cast(x) * scalingFactorX, - static_cast(y + 1) * scalingFactorY); - vertices[arrayIndexBase + 1].color = getTerrainColor(getElevation(x, y + 1), getMoisture(x, y + 1)); - vertices[arrayIndexBase + 1].texCoords = computeNormal(getElevation(x - 1, y + 1), - getElevation(x + 1, y + 1), - getElevation(x, y + 2), - getElevation(x, y)); + vertices[arrayIndexBase + 1] = computeVertex({x, y + 1}); } // Bottom right corner (first triangle) - vertices[arrayIndexBase + 2].position = sf::Vector2f(static_cast(x + 1) * scalingFactorX, - static_cast(y + 1) * scalingFactorY); - vertices[arrayIndexBase + 2].color = getTerrainColor(getElevation(x + 1, y + 1), getMoisture(x + 1, y + 1)); - vertices[arrayIndexBase + 2].texCoords = computeNormal(getElevation(x, y + 1), - getElevation(x + 2, y + 1), - getElevation(x + 1, y + 2), - getElevation(x + 1, y)); + vertices[arrayIndexBase + 2] = computeVertex({x + 1, y + 1}); // Top left corner (second triangle) vertices[arrayIndexBase + 3] = vertices[arrayIndexBase + 0]; @@ -538,25 +516,19 @@ void processWorkItem(std::vector& vertices, const WorkItem& workItem // Top right corner (second triangle) if (y > rowStart) { - vertices[arrayIndexBase + 5] = vertices[arrayIndexBase - resolutionX * 6 + 2]; + vertices[arrayIndexBase + 5] = vertices[arrayIndexBase - resolution.x * 6 + 2]; } else { - vertices[arrayIndexBase + 5].position = sf::Vector2f(static_cast(x + 1) * scalingFactorX, - static_cast(y) * scalingFactorY); - vertices[arrayIndexBase + 5].color = getTerrainColor(getElevation(x + 1, y), getMoisture(x + 1, y)); - vertices[arrayIndexBase + 5].texCoords = computeNormal(getElevation(x, y), - getElevation(x + 2, y), - getElevation(x + 1, y + 1), - getElevation(x + 1, y - 1)); + vertices[arrayIndexBase + 5] = computeVertex({x + 1, y}); } } } // Copy the resulting geometry from our thread-local buffer into the target buffer - std::memcpy(workItem.targetBuffer + (resolutionX * rowStart * 6), + std::memcpy(workItem.targetBuffer + (resolution.x * rowStart * 6), vertices.data(), - sizeof(sf::Vertex) * resolutionX * rowCount * 6); + sizeof(sf::Vertex) * resolution.x * rowCount * 6); } @@ -568,9 +540,9 @@ void processWorkItem(std::vector& vertices, const WorkItem& workItem //////////////////////////////////////////////////////////// void threadFunction() { - const unsigned int rowBlockSize = (resolutionY / blockCount) + 1; + const unsigned int rowBlockSize = (resolution.y / blockCount) + 1; - std::vector vertices(resolutionX * rowBlockSize * 6); + std::vector vertices(resolution.x * rowBlockSize * 6); WorkItem workItem = {nullptr, 0}; diff --git a/include/SFML/Audio/SoundStream.hpp b/include/SFML/Audio/SoundStream.hpp index 0654b0580..12ee9c711 100644 --- a/include/SFML/Audio/SoundStream.hpp +++ b/include/SFML/Audio/SoundStream.hpp @@ -346,11 +346,11 @@ private: /// { /// // Open the source and get audio settings /// ... -/// unsigned int channelCount = ...; -/// unsigned int sampleRate = ...; +/// unsigned int channelCount = 2; // Stereo +/// unsigned int sampleRate = 44100; // 44100 Hz /// /// // Initialize the stream -- important! -/// initialize(channelCount, sampleRate); +/// initialize(channelCount, sampleRate, {sf::SoundChannel::FrontLeft, sf::SoundChannel::FrontRight}); /// return true; /// } /// diff --git a/include/SFML/Window/Window.hpp b/include/SFML/Window/Window.hpp index 62e20a82d..2cc0da4e8 100644 --- a/include/SFML/Window/Window.hpp +++ b/include/SFML/Window/Window.hpp @@ -180,9 +180,8 @@ public: /// If \a `state` is `State::Fullscreen`, then \a `mode` must be /// a valid video mode. /// - /// The last parameter is an optional structure specifying - /// advanced OpenGL context settings such as anti-aliasing, - /// depth-buffer bits, etc. + /// The last parameter is a structure specifying advanced OpenGL + /// context settings such as anti-aliasing, depth-buffer bits, etc. /// /// \param mode Video mode to use (defines the width, height and depth of the rendering area of the window) /// \param title Title of the window @@ -193,6 +192,38 @@ public: //////////////////////////////////////////////////////////// virtual void create(VideoMode mode, const String& title, std::uint32_t style, State state, const ContextSettings& settings); + //////////////////////////////////////////////////////////// + /// \brief Create (or recreate) the window + /// + /// If the window was already created, it closes it first. + /// If \a `state` is `State::Fullscreen`, then \a `mode` must be + /// a valid video mode. + /// + /// \param mode Video mode to use (defines the width, height and depth of the rendering area of the window) + /// \param title Title of the window + /// \param state %Window state + /// + //////////////////////////////////////////////////////////// + void create(VideoMode mode, const String& title, State state) override; + + //////////////////////////////////////////////////////////// + /// \brief Create (or recreate) the window + /// + /// If the window was already created, it closes it first. + /// If \a `state` is `State::Fullscreen`, then \a `mode` must be + /// a valid video mode. + /// + /// The last parameter is a structure specifying advanced OpenGL + /// context settings such as anti-aliasing, depth-buffer bits, etc. + /// + /// \param mode Video mode to use (defines the width, height and depth of the rendering area of the window) + /// \param title Title of the window + /// \param state %Window state + /// \param settings Additional settings for the underlying OpenGL context + /// + //////////////////////////////////////////////////////////// + virtual void create(VideoMode mode, const String& title, State state, const ContextSettings& settings); + //////////////////////////////////////////////////////////// /// \brief Create (or recreate) the window from an existing control /// diff --git a/include/SFML/Window/WindowBase.hpp b/include/SFML/Window/WindowBase.hpp index 7161bed76..33d105165 100644 --- a/include/SFML/Window/WindowBase.hpp +++ b/include/SFML/Window/WindowBase.hpp @@ -157,6 +157,20 @@ public: //////////////////////////////////////////////////////////// virtual void create(VideoMode mode, const String& title, std::uint32_t style = Style::Default, State state = State::Windowed); + //////////////////////////////////////////////////////////// + /// \brief Create (or recreate) the window + /// + /// If the window was already created, it closes it first. + /// If \a `state` is `State::Fullscreen`, then \a `mode` must be + /// a valid video mode. + /// + /// \param mode Video mode to use (defines the width, height and depth of the rendering area of the window) + /// \param title Title of the window + /// \param state %Window state + /// + //////////////////////////////////////////////////////////// + virtual void create(VideoMode mode, const String& title, State state); + //////////////////////////////////////////////////////////// /// \brief Create (or recreate) the window from an existing control /// diff --git a/src/SFML/Audio/SoundFileReaderFlac.cpp b/src/SFML/Audio/SoundFileReaderFlac.cpp index 21875adca..719012e6e 100644 --- a/src/SFML/Audio/SoundFileReaderFlac.cpp +++ b/src/SFML/Audio/SoundFileReaderFlac.cpp @@ -362,13 +362,10 @@ std::uint64_t SoundFileReaderFlac::read(std::int16_t* samples, std::uint64_t max if (left > maxCount) { // There are more leftovers than needed - std::copy(m_clientData.leftovers.begin(), - m_clientData.leftovers.begin() + static_cast::difference_type>(maxCount), - samples); - std::vector leftovers(m_clientData.leftovers.begin() + - static_cast::difference_type>(maxCount), - m_clientData.leftovers.end()); - m_clientData.leftovers.swap(leftovers); + const auto signedMaxCount = static_cast::difference_type>(maxCount); + std::copy(m_clientData.leftovers.begin(), m_clientData.leftovers.begin() + signedMaxCount, samples); + m_clientData.leftovers = std::vector(m_clientData.leftovers.begin() + signedMaxCount, + m_clientData.leftovers.end()); return maxCount; } diff --git a/src/SFML/Graphics/GLCheck.cpp b/src/SFML/Graphics/GLCheck.cpp index 7a181959c..664882ea1 100644 --- a/src/SFML/Graphics/GLCheck.cpp +++ b/src/SFML/Graphics/GLCheck.cpp @@ -31,81 +31,52 @@ #include #include -#include namespace sf::priv { //////////////////////////////////////////////////////////// -void glCheckError(const std::filesystem::path& file, unsigned int line, std::string_view expression) +bool glCheckError(const std::filesystem::path& file, unsigned int line, std::string_view expression) { - // Get the last error - const GLenum errorCode = glGetError(); - - if (errorCode != GL_NO_ERROR) + const auto logError = [&](const char* error, const char* description) { - std::string error = "Unknown error"; - std::string description = "No description"; - - // Decode the error code - switch (errorCode) - { - case GL_INVALID_ENUM: - { - error = "GL_INVALID_ENUM"; - description = "An unacceptable value has been specified for an enumerated argument."; - break; - } - - case GL_INVALID_VALUE: - { - error = "GL_INVALID_VALUE"; - description = "A numeric argument is out of range."; - break; - } - - case GL_INVALID_OPERATION: - { - error = "GL_INVALID_OPERATION"; - description = "The specified operation is not allowed in the current state."; - break; - } - - case GL_STACK_OVERFLOW: - { - error = "GL_STACK_OVERFLOW"; - description = "This command would cause a stack overflow."; - break; - } - - case GL_STACK_UNDERFLOW: - { - error = "GL_STACK_UNDERFLOW"; - description = "This command would cause a stack underflow."; - break; - } - - case GL_OUT_OF_MEMORY: - { - error = "GL_OUT_OF_MEMORY"; - description = "There is not enough memory left to execute the command."; - break; - } - - case GLEXT_GL_INVALID_FRAMEBUFFER_OPERATION: - { - error = "GL_INVALID_FRAMEBUFFER_OPERATION"; - description = "The object bound to FRAMEBUFFER_BINDING is not \"framebuffer complete\"."; - break; - } - } - - // Log the error err() << "An internal OpenGL call failed in " << file.filename() << "(" << line << ")." << "\nExpression:\n " << expression << "\nError description:\n " << error << "\n " << description << '\n' << std::endl; + + return false; + }; + + switch (glGetError()) + { + case GL_NO_ERROR: + return true; + + case GL_INVALID_ENUM: + return logError("GL_INVALID_ENUM", "An unacceptable value has been specified for an enumerated argument."); + + case GL_INVALID_VALUE: + return logError("GL_INVALID_VALUE", "A numeric argument is out of range."); + + case GL_INVALID_OPERATION: + return logError("GL_INVALID_OPERATION", "The specified operation is not allowed in the current state."); + + case GL_STACK_OVERFLOW: + return logError("GL_STACK_OVERFLOW", "This command would cause a stack overflow."); + + case GL_STACK_UNDERFLOW: + return logError("GL_STACK_UNDERFLOW", "This command would cause a stack underflow."); + + case GL_OUT_OF_MEMORY: + return logError("GL_OUT_OF_MEMORY", "There is not enough memory left to execute the command."); + + case GLEXT_GL_INVALID_FRAMEBUFFER_OPERATION: + return logError("GL_INVALID_FRAMEBUFFER_OPERATION", + "The object bound to FRAMEBUFFER_BINDING is not \"framebuffer complete\"."); + + default: + return logError("Unknown error", "Unknown description"); } } - } // namespace sf::priv diff --git a/src/SFML/Graphics/GLCheck.hpp b/src/SFML/Graphics/GLCheck.hpp index eb1e0366e..fb2799120 100644 --- a/src/SFML/Graphics/GLCheck.hpp +++ b/src/SFML/Graphics/GLCheck.hpp @@ -63,7 +63,9 @@ namespace sf::priv /// \param line Line number of the source file where the call is located /// \param expression The evaluated expression as a string /// +/// \return `false` if an error occurred, `true` otherwise +/// //////////////////////////////////////////////////////////// -void glCheckError(const std::filesystem::path& file, unsigned int line, std::string_view expression); +bool glCheckError(const std::filesystem::path& file, unsigned int line, std::string_view expression); } // namespace sf::priv diff --git a/src/SFML/Graphics/Image.cpp b/src/SFML/Graphics/Image.cpp index 02a989829..251aa71e1 100644 --- a/src/SFML/Graphics/Image.cpp +++ b/src/SFML/Graphics/Image.cpp @@ -46,6 +46,7 @@ #include #include #include +#include #include #include @@ -154,7 +155,7 @@ void Image::resize(Vector2u size, Color color) } // Commit the new pixel buffer - m_pixels.swap(newPixels); + m_pixels = std::move(newPixels); // Assign the new size m_size = size; @@ -179,7 +180,7 @@ void Image::resize(Vector2u size, const std::uint8_t* pixels) std::vector newPixels(pixels, pixels + size.x * size.y * 4); // Commit the new pixel buffer - m_pixels.swap(newPixels); + m_pixels = std::move(newPixels); // Assign the new size m_size = size; diff --git a/src/SFML/Graphics/Text.cpp b/src/SFML/Graphics/Text.cpp index c59db2ee3..27f3912ec 100644 --- a/src/SFML/Graphics/Text.cpp +++ b/src/SFML/Graphics/Text.cpp @@ -312,9 +312,7 @@ Vector2f Text::findCharacterPos(std::size_t index) const } // Transform the position to global coordinates - position = getTransform().transformPoint(position); - - return position; + return getTransform().transformPoint(position); } diff --git a/src/SFML/Window/EGLCheck.cpp b/src/SFML/Window/EGLCheck.cpp index 8f050d8d0..26c54475f 100644 --- a/src/SFML/Window/EGLCheck.cpp +++ b/src/SFML/Window/EGLCheck.cpp @@ -33,139 +33,88 @@ #include #include -#include namespace sf::priv { //////////////////////////////////////////////////////////// -void eglCheckError(const std::filesystem::path& file, unsigned int line, std::string_view expression) +bool eglCheckError(const std::filesystem::path& file, unsigned int line, std::string_view expression) { - // Obtain information about the success or failure of the most recent EGL - // function called in the current thread - const EGLint errorCode = eglGetError(); - - if (errorCode != EGL_SUCCESS) + const auto logError = [&](const char* error, const char* description) { - std::string error = "unknown error"; - std::string description = "no description"; - - // Decode the error code returned - switch (errorCode) - { - case EGL_NOT_INITIALIZED: - { - error = "EGL_NOT_INITIALIZED"; - description = "EGL is not initialized, or could not be initialized, for the specified display"; - break; - } - - case EGL_BAD_ACCESS: - { - error = "EGL_BAD_ACCESS"; - description = - "EGL cannot access a requested resource (for example, a context is bound in another thread)"; - break; - } - - case EGL_BAD_ALLOC: - { - error = "EGL_BAD_ALLOC"; - description = "EGL failed to allocate resources for the requested operation"; - break; - } - case EGL_BAD_ATTRIBUTE: - { - error = "EGL_BAD_ATTRIBUTE"; - description = "an unrecognized attribute or attribute value was passed in an attribute list"; - break; - } - - case EGL_BAD_CONTEXT: - { - error = "EGL_BAD_CONTEXT"; - description = "an EGLContext argument does not name a valid EGLContext"; - break; - } - - case EGL_BAD_CONFIG: - { - error = "EGL_BAD_CONFIG"; - description = "an EGLConfig argument does not name a valid EGLConfig"; - break; - } - - case EGL_BAD_CURRENT_SURFACE: - { - error = "EGL_BAD_CURRENT_SURFACE"; - description = - "the current surface of the calling thread is a window, pbuffer, or pixmap that is no longer valid"; - break; - } - - case EGL_BAD_DISPLAY: - { - error = "EGL_BAD_DISPLAY"; - description = - "an EGLDisplay argument does not name a valid EGLDisplay; or, EGL is not initialized on the " - "specified EGLDisplay"; - break; - } - - - case EGL_BAD_SURFACE: - { - error = "EGL_BAD_SURFACE"; - description = - "an EGLSurface argument does not name a valid surface (window, pbuffer, or pixmap) configured for " - "rendering"; - break; - } - - case EGL_BAD_MATCH: - { - error = "EGL_BAD_MATCH"; - description = - "arguments are inconsistent; for example, an otherwise valid context requires buffers (e.g. depth " - "or stencil) not allocated by an otherwise valid surface"; - break; - } - - case EGL_BAD_PARAMETER: - { - error = "EGL_BAD_PARAMETER"; - description = "one or more argument values are invalid"; - break; - } - - case EGL_BAD_NATIVE_PIXMAP: - { - error = "EGL_BAD_NATIVE_PIXMAP"; - description = "an EGLNativePixmapType argument does not refer to a valid native pixmap"; - break; - } - - case EGL_BAD_NATIVE_WINDOW: - { - error = "EGL_BAD_NATIVE_WINDOW"; - description = "an EGLNativeWindowType argument does not refer to a valid native window"; - break; - } - - case EGL_CONTEXT_LOST: - { - error = "EGL_CONTEXT_LOST"; - description = - "a power management event has occurred. The application must destroy all contexts and reinitialize " - "client API state and objects to continue rendering"; - break; - } - } - - // Log the error err() << "An internal EGL call failed in " << file.filename() << " (" << line << ") : " << "\nExpression:\n " << expression << "\nError description:\n " << error << "\n " << description << '\n' << std::endl; + + return false; + }; + + switch (eglGetError()) + { + case EGL_SUCCESS: + return true; + + case EGL_NOT_INITIALIZED: + return logError("EGL_NOT_INITIALIZED", + "EGL is not initialized, or could not be initialized, for the specified display"); + + case EGL_BAD_ACCESS: + return logError("EGL_BAD_ACCESS", + "EGL cannot access a requested resource (for example, a context is bound in another " + "thread)"); + + case EGL_BAD_ALLOC: + return logError("EGL_BAD_ALLOC", "EGL failed to allocate resources for the requested operation"); + + case EGL_BAD_ATTRIBUTE: + return logError("EGL_BAD_ATTRIBUTE", + "an unrecognized attribute or attribute value was passed in an attribute list"); + + case EGL_BAD_CONTEXT: + return logError("EGL_BAD_CONTEXT", "an EGLContext argument does not name a valid EGLContext"); + + case EGL_BAD_CONFIG: + return logError("EGL_BAD_CONFIG", "an EGLConfig argument does not name a valid EGLConfig"); + + case EGL_BAD_CURRENT_SURFACE: + return logError("EGL_BAD_CURRENT_SURFACE", + "the current surface of the calling thread is a window, pbuffer, or pixmap that is no " + "longer valid"); + + case EGL_BAD_DISPLAY: + return logError("EGL_BAD_DISPLAY", + "an EGLDisplay argument does not name a valid EGLDisplay; or, EGL is not initialized on " + "the specified EGLDisplay"); + + + case EGL_BAD_SURFACE: + return logError("EGL_BAD_SURFACE", + "an EGLSurface argument does not name a valid surface (window, pbuffer, or pixmap) " + "configured for rendering"); + + case EGL_BAD_MATCH: + return logError("EGL_BAD_MATCH", + "arguments are inconsistent; for example, an otherwise valid context requires buffers " + "(e.g. depth or stencil) not allocated by an otherwise valid surface"); + + case EGL_BAD_PARAMETER: + return logError("EGL_BAD_PARAMETER", "one or more argument values are invalid"); + + case EGL_BAD_NATIVE_PIXMAP: + return logError("EGL_BAD_NATIVE_PIXMAP", + "an EGLNativePixmapType argument does not refer to a valid native pixmap"); + + case EGL_BAD_NATIVE_WINDOW: + return logError("EGL_BAD_NATIVE_WINDOW", + "an EGLNativeWindowType argument does not refer to a valid native window"); + + case EGL_CONTEXT_LOST: + return logError("EGL_CONTEXT_LOST", + "a power management event has occurred. The application must destroy all contexts and " + "reinitialize client API state and objects to continue rendering"); + + default: + return logError("Unknown error", "Unknown description"); } } diff --git a/src/SFML/Window/EGLCheck.hpp b/src/SFML/Window/EGLCheck.hpp index 1f63ce1c1..68e4f0722 100644 --- a/src/SFML/Window/EGLCheck.hpp +++ b/src/SFML/Window/EGLCheck.hpp @@ -63,7 +63,9 @@ namespace sf::priv /// \param line Line number of the source file where the call is located /// \param expression The evaluated expression as a string /// +/// \return `false` if an error occurred, `true` otherwise +/// //////////////////////////////////////////////////////////// -void eglCheckError(const std::filesystem::path& file, unsigned int line, std::string_view expression); +bool eglCheckError(const std::filesystem::path& file, unsigned int line, std::string_view expression); } // namespace sf::priv diff --git a/src/SFML/Window/Window.cpp b/src/SFML/Window/Window.cpp index fc0cc6291..54d6ac3bd 100644 --- a/src/SFML/Window/Window.cpp +++ b/src/SFML/Window/Window.cpp @@ -95,6 +95,20 @@ void Window::create(VideoMode mode, const String& title, std::uint32_t style, St } +//////////////////////////////////////////////////////////// +void Window::create(VideoMode mode, const String& title, State state) +{ + Window::create(mode, title, sf::Style::Default, state, ContextSettings{}); +} + + +//////////////////////////////////////////////////////////// +void Window::create(VideoMode mode, const String& title, State state, const ContextSettings& settings) +{ + Window::create(mode, title, sf::Style::Default, state, settings); +} + + //////////////////////////////////////////////////////////// void Window::create(WindowHandle handle) { diff --git a/src/SFML/Window/WindowBase.cpp b/src/SFML/Window/WindowBase.cpp index 9d925f833..f16d9513a 100644 --- a/src/SFML/Window/WindowBase.cpp +++ b/src/SFML/Window/WindowBase.cpp @@ -102,6 +102,13 @@ void WindowBase::create(VideoMode mode, const String& title, std::uint32_t style } +//////////////////////////////////////////////////////////// +void WindowBase::create(VideoMode mode, const String& title, State state) +{ + create(mode, title, Style::Default, state); +} + + //////////////////////////////////////////////////////////// void WindowBase::create(WindowHandle handle) { diff --git a/src/SFML/Window/macOS/HIDInputManager.hpp b/src/SFML/Window/macOS/HIDInputManager.hpp index 52397b11f..2b078fd0c 100644 --- a/src/SFML/Window/macOS/HIDInputManager.hpp +++ b/src/SFML/Window/macOS/HIDInputManager.hpp @@ -286,7 +286,8 @@ private: //////////////////////////////////////////////////////////// // Member data //////////////////////////////////////////////////////////// - IOHIDManagerRef m_manager{}; ///< Underlying HID Manager + IOHIDManagerRef m_manager{}; ///< Underlying HID Manager + bool m_keysInitialized{}; ///< Has initializeKeyboard been called at least once? EnumArray m_keys; ///< All the keys on any connected keyboard EnumArray m_keyToScancodeMapping{}; ///< Mapping from Key to Scancode EnumArray m_scancodeToKeyMapping{}; ///< Mapping from Scancode to Key diff --git a/src/SFML/Window/macOS/HIDInputManager.mm b/src/SFML/Window/macOS/HIDInputManager.mm index 05bd9ec57..829901f55 100644 --- a/src/SFML/Window/macOS/HIDInputManager.mm +++ b/src/SFML/Window/macOS/HIDInputManager.mm @@ -562,6 +562,13 @@ bool HIDInputManager::isKeyPressed(Keyboard::Key key) //////////////////////////////////////////////////////////// bool HIDInputManager::isKeyPressed(Keyboard::Scancode code) { + // Lazy load m_keys to prevent unnecessary macOS input monitoring permission requests + if (!m_keysInitialized) + { + initializeKeyboard(); + m_keysInitialized = true; + } + return (code != Keyboard::Scan::Unknown) && isPressed(m_keys[code]); } @@ -716,7 +723,6 @@ HIDInputManager::HIDInputManager() } // Build up our knowledge of the hardware - initializeKeyboard(); buildMappings(); // Register for notification on keyboard layout changes @@ -924,13 +930,17 @@ void HIDInputManager::freeUp() m_manager = nil; - for (auto& key : m_keys) + if (m_keysInitialized) { - for (IOHIDElementRef iohidElementRef : key) - CFRelease(iohidElementRef); + for (auto& key : m_keys) + { + for (IOHIDElementRef iohidElementRef : key) + CFRelease(iohidElementRef); - key.clear(); + key.clear(); + } } + m_keysInitialized = false; } diff --git a/test/Window/Window.test.cpp b/test/Window/Window.test.cpp index b48e91705..f5443d7ae 100644 --- a/test/Window/Window.test.cpp +++ b/test/Window/Window.test.cpp @@ -128,7 +128,16 @@ TEST_CASE("[Window] sf::Window", runDisplayTests()) CHECK(window.getSettings().attributeFlags == sf::ContextSettings::Default); } - SECTION("Mode, title, style, and context settings") + SECTION("Mode, title, style, and state") + { + window.create(sf::VideoMode({240, 360}), "Window Tests", sf::Style::Resize, sf::State::Windowed); + CHECK(window.isOpen()); + CHECK(window.getSize() == sf::Vector2u(240, 360)); + CHECK(window.getNativeHandle() != sf::WindowHandle()); + CHECK(window.getSettings().attributeFlags == sf::ContextSettings::Default); + } + + SECTION("Mode, title, style, state, and context settings") { window.create(sf::VideoMode({240, 360}), "Window Tests", @@ -142,5 +151,28 @@ TEST_CASE("[Window] sf::Window", runDisplayTests()) CHECK(window.getSettings().stencilBits >= 1); CHECK(window.getSettings().antiAliasingLevel >= 1); } + + SECTION("Mode, title, and state") + { + window.create(sf::VideoMode({240, 360}), "Window Tests", sf::State::Windowed); + CHECK(window.isOpen()); + CHECK(window.getSize() == sf::Vector2u(240, 360)); + CHECK(window.getNativeHandle() != sf::WindowHandle()); + CHECK(window.getSettings().attributeFlags == sf::ContextSettings::Default); + } + + SECTION("Mode, title, state, and context settings") + { + window.create(sf::VideoMode({240, 360}), + "Window Tests", + sf::State::Windowed, + sf::ContextSettings{/* depthBits*/ 1, /* stencilBits */ 1, /* antiAliasingLevel */ 1}); + CHECK(window.isOpen()); + CHECK(window.getSize() == sf::Vector2u(240, 360)); + CHECK(window.getNativeHandle() != sf::WindowHandle()); + CHECK(window.getSettings().depthBits >= 1); + CHECK(window.getSettings().stencilBits >= 1); + CHECK(window.getSettings().antiAliasingLevel >= 1); + } } } diff --git a/test/Window/WindowBase.test.cpp b/test/Window/WindowBase.test.cpp index d9dcfb3e3..66b01befc 100644 --- a/test/Window/WindowBase.test.cpp +++ b/test/Window/WindowBase.test.cpp @@ -88,6 +88,14 @@ TEST_CASE("[Window] sf::WindowBase", runDisplayTests()) CHECK(windowBase.getNativeHandle() != sf::WindowHandle()); } + SECTION("Mode, title, and state") + { + windowBase.create(sf::VideoMode({240, 360}), "WindowBase Tests", sf::State::Windowed); + CHECK(windowBase.isOpen()); + CHECK(windowBase.getSize() == sf::Vector2u(240, 360)); + CHECK(windowBase.getNativeHandle() != sf::WindowHandle()); + } + SECTION("Mode, title, style, and state") { windowBase.create(sf::VideoMode({240, 360}), "WindowBase Tests", sf::Style::Resize, sf::State::Windowed);