From 8967e202eb1bdb87b99c036d5c4e7ffcd1c89054 Mon Sep 17 00:00:00 2001 From: Jan Niklas Hasse Date: Sun, 27 Oct 2024 21:06:47 +0100 Subject: [PATCH] ci: Enforce Linux line-endings --- .github/ci.py | 42 ++++++++ .github/workflows/linux.yml | 2 + .gitlab-ci.yml | 1 + cmake/FindVorbisFile.cmake | 4 +- examples/bike/wheel.cpp | 66 ++++++------ src/jngl/Channel.hpp | 196 ++++++++++++++++++------------------ src/jngl/init.hpp | 5 +- src/png/ImageDataPNG.cpp | 176 ++++++++++++++++---------------- src/png/ImageDataPNG.hpp | 72 ++++++------- 9 files changed, 305 insertions(+), 259 deletions(-) create mode 100755 .github/ci.py diff --git a/.github/ci.py b/.github/ci.py new file mode 100755 index 000000000..3c79a0a5a --- /dev/null +++ b/.github/ci.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python3 + +import os + +ignores = [ + '.git/', + 'build/', + 'glad/', + 'include/', + '.gitlab-ci-local/', + '.cache/', +] + +error_count = 0 + +def error(path: str, msg: str) -> None: + global error_count + error_count += 1 + print('\x1b[1;31m{}\x1b[0;31m{}\x1b[0m'.format(path, msg)) + +for root, directory, filenames in os.walk('.'): + for filename in filenames: + path = os.path.join(root, filename)[2:] + if any([path.startswith(x) for x in ignores]): + continue + with open(path, 'rb') as file: + line_nr = 1 + try: + for line in [x.decode() for x in file.readlines()]: + if len(line) == 0 or line[-1] != '\n': + error(path, ' missing newline at end of file.') + if len(line) > 1: + if line[-2] == '\r': + error(path, ' has Windows line endings.') + break + if line[-2] == ' ' or line[-2] == '\t': + error(path, ':{} has trailing whitespace.'.format(line_nr)) + line_nr += 1 + except UnicodeError: + pass # binary file + +exit(error_count) diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 8a97b3102..7342ee01a 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -5,6 +5,8 @@ jobs: runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 + - name: Basic linting + run: .github/ci.py - name: Install dependencies run: | sudo apt update diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 1a2306567..26c43c47b 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -12,6 +12,7 @@ linux: stage: build script: - microdnf install -y cmake ninja-build gcc-c++ fontconfig-devel freetype-devel libvorbis-devel libwebp-devel SDL2-devel libtheora-devel clang util-linux >/dev/null + - .github/ci.py - CXXFLAGS=-fdiagnostics-color cmake -Bbuild -GNinja - cd build - ninja diff --git a/cmake/FindVorbisFile.cmake b/cmake/FindVorbisFile.cmake index a8e7cf78f..0fcdd8d86 100644 --- a/cmake/FindVorbisFile.cmake +++ b/cmake/FindVorbisFile.cmake @@ -29,7 +29,7 @@ FIND_PATH(VorbisFile_INCLUDE_DIRS /opt ) -FIND_LIBRARY(VorbisFile_LIBRARIES +FIND_LIBRARY(VorbisFile_LIBRARIES NAMES vorbisfile HINTS $ENV{VORBISDIR} @@ -47,7 +47,7 @@ FIND_LIBRARY(VorbisFile_LIBRARIES /opt ) -FIND_LIBRARY(VorbisFile_LIBRARIES_DEBUG +FIND_LIBRARY(VorbisFile_LIBRARIES_DEBUG NAMES VorbisFile_d HINTS $ENV{VORBISDIR} diff --git a/examples/bike/wheel.cpp b/examples/bike/wheel.cpp index 168176614..a187e8795 100644 --- a/examples/bike/wheel.cpp +++ b/examples/bike/wheel.cpp @@ -1,33 +1,33 @@ -#include "wheel.hpp" - -#include - -void Wheel::CollisionWith(const jngl::Vec2& collisionPoint) { - jngl::Vec2 perpendicular = position_ - collisionPoint; - boost::qvm::normalize(perpendicular); - double temp2 = -1.5 * boost::qvm::dot(perpendicular, speed_); - jngl::Vec2 temp = temp2 * perpendicular; - speed_ += temp; - - const double distance = boost::qvm::mag(collisionPoint - position_); - jngl::Vec2 correction = perpendicular * (radius_ - distance + 0.1); - position_ += correction; - otherWheel_->position_ += correction; -} - -void Wheel::Move() -{ - speed_ += jngl::Vec2(0, 0.04); // Schwerkraft - speed_ *= 0.99; // Luftreibung -} - -void Wheel::draw() const { - jngl::draw("wheel", static_cast(position_.x - radius_), - static_cast(position_.y - radius_)); -} - -Wheel::Wheel(const double x, const double y) : position_(x, y) -{ -} - -const int Wheel::radius_ = 32; +#include "wheel.hpp" + +#include + +void Wheel::CollisionWith(const jngl::Vec2& collisionPoint) { + jngl::Vec2 perpendicular = position_ - collisionPoint; + boost::qvm::normalize(perpendicular); + double temp2 = -1.5 * boost::qvm::dot(perpendicular, speed_); + jngl::Vec2 temp = temp2 * perpendicular; + speed_ += temp; + + const double distance = boost::qvm::mag(collisionPoint - position_); + jngl::Vec2 correction = perpendicular * (radius_ - distance + 0.1); + position_ += correction; + otherWheel_->position_ += correction; +} + +void Wheel::Move() +{ + speed_ += jngl::Vec2(0, 0.04); // Schwerkraft + speed_ *= 0.99; // Luftreibung +} + +void Wheel::draw() const { + jngl::draw("wheel", static_cast(position_.x - radius_), + static_cast(position_.y - radius_)); +} + +Wheel::Wheel(const double x, const double y) : position_(x, y) +{ +} + +const int Wheel::radius_ = 32; diff --git a/src/jngl/Channel.hpp b/src/jngl/Channel.hpp index bbaff5dee..859e5eba4 100644 --- a/src/jngl/Channel.hpp +++ b/src/jngl/Channel.hpp @@ -1,98 +1,98 @@ -// Copyright 2024 Jan Niklas Hasse -// For conditions of distribution and use, see copyright notice in LICENSE.txt -/// Contains jngl::Channel class -/// \file - -#include "Finally.hpp" - -#include -#include - -namespace jngl { - -class SoundFile; -struct Stream; - -/// An audio channel, different channels could be for example: "Music", "Speech" and "Sound Effects" -/// -/// Example: -/// \code -/// struct Channels : public jngl::Singleton { -/// jngl::Channel& main = jngl::Channel::main(); -/// jngl::Channel music; -/// jngl::Channel speech; -/// }; -/// -/// // somewhere else: -/// Channels::handle().music.loop("background_music01.ogg"); -/// \endcode -class Channel { -public: - Channel(); - ~Channel(); - - /// Play OGG file on this channel - void play(const std::string& filename); - - /// Play an OGG audio file on this channel in a loop - /// - /// If it's already playing, this function won't play it twice. - std::shared_ptr loop(const std::string& filename); - - /// Stop OGG file playing on this channel - void stop(const std::string& filename); - - /// Pauses the Channel; destroying the returned Finally object will resume again - /// - /// Note that this doesn't change the status of jngl::isPlaying as that only depends on the - /// status of the SoundFile. - /// - /// Example where you want to pause audio in a menu: - /// \code - /// class PauseMenu : public jngl::Work { - /// jngl::Finally paused; - /// - /// public: - /// PauseMenu() : paused(jngl::Channel::main().pause()) {} - /// void step() override { /* ... */ } - /// void draw() const override { /* ... */ } - /// }; - /// \endcode - /// - /// Or if you want to toggle pausing audio using Space: - /// \code - /// class PauseExample : public jngl::Work { - /// std::optional paused; - /// - /// public: - /// void step() override { - /// if (jngl::keyPressed(jngl::key::Space)) { - /// if (paused) { - /// paused = {}; // calls ~Finally() and resumes playback - /// } else { - /// paused = jngl::Channel::main().pause(); - /// } - /// } - /// } - /// void draw() const override { /* ... */ } - /// }; - /// \endcode - [[nodiscard]] Finally pause(); - - /// Set volume of this channel in [0, ∞]. Default is 1.0f - void setVolume(float volume); - - /// Returns the main Channel on which jngl::play, jngl::loop, jngl::stop operate - static Channel& main(); - - /// Internal function for now - void add(std::shared_ptr); - /// Internal function for now - void remove(const Stream*); - -private: - struct Impl; - std::unique_ptr impl; -}; - -} // namespace jngl +// Copyright 2024 Jan Niklas Hasse +// For conditions of distribution and use, see copyright notice in LICENSE.txt +/// Contains jngl::Channel class +/// \file + +#include "Finally.hpp" + +#include +#include + +namespace jngl { + +class SoundFile; +struct Stream; + +/// An audio channel, different channels could be for example: "Music", "Speech" and "Sound Effects" +/// +/// Example: +/// \code +/// struct Channels : public jngl::Singleton { +/// jngl::Channel& main = jngl::Channel::main(); +/// jngl::Channel music; +/// jngl::Channel speech; +/// }; +/// +/// // somewhere else: +/// Channels::handle().music.loop("background_music01.ogg"); +/// \endcode +class Channel { +public: + Channel(); + ~Channel(); + + /// Play OGG file on this channel + void play(const std::string& filename); + + /// Play an OGG audio file on this channel in a loop + /// + /// If it's already playing, this function won't play it twice. + std::shared_ptr loop(const std::string& filename); + + /// Stop OGG file playing on this channel + void stop(const std::string& filename); + + /// Pauses the Channel; destroying the returned Finally object will resume again + /// + /// Note that this doesn't change the status of jngl::isPlaying as that only depends on the + /// status of the SoundFile. + /// + /// Example where you want to pause audio in a menu: + /// \code + /// class PauseMenu : public jngl::Work { + /// jngl::Finally paused; + /// + /// public: + /// PauseMenu() : paused(jngl::Channel::main().pause()) {} + /// void step() override { /* ... */ } + /// void draw() const override { /* ... */ } + /// }; + /// \endcode + /// + /// Or if you want to toggle pausing audio using Space: + /// \code + /// class PauseExample : public jngl::Work { + /// std::optional paused; + /// + /// public: + /// void step() override { + /// if (jngl::keyPressed(jngl::key::Space)) { + /// if (paused) { + /// paused = {}; // calls ~Finally() and resumes playback + /// } else { + /// paused = jngl::Channel::main().pause(); + /// } + /// } + /// } + /// void draw() const override { /* ... */ } + /// }; + /// \endcode + [[nodiscard]] Finally pause(); + + /// Set volume of this channel in [0, ∞]. Default is 1.0f + void setVolume(float volume); + + /// Returns the main Channel on which jngl::play, jngl::loop, jngl::stop operate + static Channel& main(); + + /// Internal function for now + void add(std::shared_ptr); + /// Internal function for now + void remove(const Stream*); + +private: + struct Impl; + std::unique_ptr impl; +}; + +} // namespace jngl diff --git a/src/jngl/init.hpp b/src/jngl/init.hpp index 138a3445b..3f4046671 100644 --- a/src/jngl/init.hpp +++ b/src/jngl/init.hpp @@ -33,7 +33,7 @@ void mainLoop(AppParameters); /// jngl::AppParameters params; /// params.displayName = "My Game"; /// params.screenSize = { 1920, 1080 }; -/// +/// /// params.start = []() { /// return std::make_shared(); /// }; @@ -53,7 +53,8 @@ JNGL_MAIN_BEGIN { // NOLINT if (err) { std::filesystem::current_path("../../data", err); // move out of build/Debug folder if (err) { - std::filesystem::current_path("../../../data", err); // move out of out\build\x64-Debug + std::filesystem::current_path("../../../data", + err); // move out of out\build\x64-Debug if (err) { std::filesystem::current_path(jngl::getBinaryPath() + "data", err); } diff --git a/src/png/ImageDataPNG.cpp b/src/png/ImageDataPNG.cpp index 8003e3178..1da3ab657 100644 --- a/src/png/ImageDataPNG.cpp +++ b/src/png/ImageDataPNG.cpp @@ -1,88 +1,88 @@ -// Copyright 2021-2024 Jan Niklas Hasse -// For conditions of distribution and use, see copyright notice in LICENSE.txt -#include "ImageDataPNG.hpp" - -#include "../jngl/Finally.hpp" - -#include -#include -#include -#include - -namespace jngl { - -ImageDataPNG::ImageDataPNG(const std::string& filename, FILE* fp) { - if (!fp) { - throw std::runtime_error(std::string("File not found: ") + filename); - } - png_byte buf[PNG_BYTES_TO_CHECK]; - - // Read in some of the signature bytes - if (fread(buf, 1, PNG_BYTES_TO_CHECK, fp) != PNG_BYTES_TO_CHECK) { - throw std::runtime_error(std::string("Error reading signature bytes.")); - } - - assert(png_sig_cmp(buf, (png_size_t)0, PNG_BYTES_TO_CHECK) == 0); - png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); - if (!png_ptr) { - throw std::runtime_error("libpng error while reading"); - } - - png_infop info_ptr = png_create_info_struct(png_ptr); - if (!info_ptr) { - throw std::runtime_error("libpng error while reading"); - } - - if (setjmp(png_jmpbuf(png_ptr))) { - // Free all of the memory associated with the png_ptr and info_ptr - png_destroy_read_struct(&png_ptr, &info_ptr, nullptr); - throw std::runtime_error("Error reading file."); - } - png_init_io(png_ptr, fp); - png_set_sig_bytes(png_ptr, PNG_BYTES_TO_CHECK); - int colorType = png_get_color_type(png_ptr, info_ptr); - if (colorType == PNG_COLOR_TYPE_GRAY || colorType == PNG_COLOR_TYPE_GRAY_ALPHA) { - png_set_gray_to_rgb(png_ptr); - } - png_read_png(png_ptr, info_ptr, PNG_TRANSFORM_EXPAND | PNG_TRANSFORM_STRIP_16, nullptr); - Finally destroyRead( - [&png_ptr, &info_ptr]() { png_destroy_read_struct(&png_ptr, &info_ptr, nullptr); }); - - x = png_get_image_width(png_ptr, info_ptr); - y = png_get_image_height(png_ptr, info_ptr); - const int channels = png_get_channels(png_ptr, info_ptr); - if (channels != 4) { - throw std::runtime_error(filename + " has " + std::to_string(channels) + - " channels. Only PNGs with 4 channels (RGBA) are supported."); - } - - const auto row_pointers = png_get_rows(png_ptr, info_ptr); - imageData.resize(x * y * channels); - for (size_t i = 0; i < y; ++i) { - memcpy(&imageData[i * x * channels], row_pointers[i], x * channels); - } -} - -ImageDataPNG::~ImageDataPNG() = default; - -const uint8_t* ImageDataPNG::pixels() const { - return imageData.data(); -} - -int ImageDataPNG::getWidth() const { - return static_cast(x); -} - -int ImageDataPNG::getHeight() const { - return static_cast(y); -} - -int ImageDataPNG::getImageWidth() const { - return static_cast(x); -} - -int ImageDataPNG::getImageHeight() const { - return static_cast(y); -} - -} // namespace jngl +// Copyright 2021-2024 Jan Niklas Hasse +// For conditions of distribution and use, see copyright notice in LICENSE.txt +#include "ImageDataPNG.hpp" + +#include "../jngl/Finally.hpp" + +#include +#include +#include +#include + +namespace jngl { + +ImageDataPNG::ImageDataPNG(const std::string& filename, FILE* fp) { + if (!fp) { + throw std::runtime_error(std::string("File not found: ") + filename); + } + png_byte buf[PNG_BYTES_TO_CHECK]; + + // Read in some of the signature bytes + if (fread(buf, 1, PNG_BYTES_TO_CHECK, fp) != PNG_BYTES_TO_CHECK) { + throw std::runtime_error(std::string("Error reading signature bytes.")); + } + + assert(png_sig_cmp(buf, (png_size_t)0, PNG_BYTES_TO_CHECK) == 0); + png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); + if (!png_ptr) { + throw std::runtime_error("libpng error while reading"); + } + + png_infop info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) { + throw std::runtime_error("libpng error while reading"); + } + + if (setjmp(png_jmpbuf(png_ptr))) { + // Free all of the memory associated with the png_ptr and info_ptr + png_destroy_read_struct(&png_ptr, &info_ptr, nullptr); + throw std::runtime_error("Error reading file."); + } + png_init_io(png_ptr, fp); + png_set_sig_bytes(png_ptr, PNG_BYTES_TO_CHECK); + int colorType = png_get_color_type(png_ptr, info_ptr); + if (colorType == PNG_COLOR_TYPE_GRAY || colorType == PNG_COLOR_TYPE_GRAY_ALPHA) { + png_set_gray_to_rgb(png_ptr); + } + png_read_png(png_ptr, info_ptr, PNG_TRANSFORM_EXPAND | PNG_TRANSFORM_STRIP_16, nullptr); + Finally destroyRead( + [&png_ptr, &info_ptr]() { png_destroy_read_struct(&png_ptr, &info_ptr, nullptr); }); + + x = png_get_image_width(png_ptr, info_ptr); + y = png_get_image_height(png_ptr, info_ptr); + const int channels = png_get_channels(png_ptr, info_ptr); + if (channels != 4) { + throw std::runtime_error(filename + " has " + std::to_string(channels) + + " channels. Only PNGs with 4 channels (RGBA) are supported."); + } + + const auto row_pointers = png_get_rows(png_ptr, info_ptr); + imageData.resize(x * y * channels); + for (size_t i = 0; i < y; ++i) { + memcpy(&imageData[i * x * channels], row_pointers[i], x * channels); + } +} + +ImageDataPNG::~ImageDataPNG() = default; + +const uint8_t* ImageDataPNG::pixels() const { + return imageData.data(); +} + +int ImageDataPNG::getWidth() const { + return static_cast(x); +} + +int ImageDataPNG::getHeight() const { + return static_cast(y); +} + +int ImageDataPNG::getImageWidth() const { + return static_cast(x); +} + +int ImageDataPNG::getImageHeight() const { + return static_cast(y); +} + +} // namespace jngl diff --git a/src/png/ImageDataPNG.hpp b/src/png/ImageDataPNG.hpp index 55cd0a202..cac8337b8 100644 --- a/src/png/ImageDataPNG.hpp +++ b/src/png/ImageDataPNG.hpp @@ -1,36 +1,36 @@ -// Copyright 2021-2024 Jan Niklas Hasse -// For conditions of distribution and use, see copyright notice in LICENSE.txt -#pragma once - -#include "../jngl/ImageData.hpp" - -#include -#include - -namespace jngl { - -class ImageDataPNG : public ImageData { -public: - ImageDataPNG(const std::string& filename, FILE*); - ~ImageDataPNG() override; - ImageDataPNG(const ImageDataPNG&) = delete; - ImageDataPNG& operator=(const ImageDataPNG&) = delete; - ImageDataPNG(ImageDataPNG&&) noexcept; - ImageDataPNG& operator=(ImageDataPNG&&) noexcept; - - const uint8_t* pixels() const override; - int getWidth() const override; - int getHeight() const override; - - /// returns getWidth() as PNGs don't support scaling - int getImageWidth() const override; - /// returns getHeight() as PNGs don't support scaling - int getImageHeight() const override; - -private: - const static unsigned int PNG_BYTES_TO_CHECK = 4; - std::vector imageData; - size_t x, y; -}; - -} // namespace jngl +// Copyright 2021-2024 Jan Niklas Hasse +// For conditions of distribution and use, see copyright notice in LICENSE.txt +#pragma once + +#include "../jngl/ImageData.hpp" + +#include +#include + +namespace jngl { + +class ImageDataPNG : public ImageData { +public: + ImageDataPNG(const std::string& filename, FILE*); + ~ImageDataPNG() override; + ImageDataPNG(const ImageDataPNG&) = delete; + ImageDataPNG& operator=(const ImageDataPNG&) = delete; + ImageDataPNG(ImageDataPNG&&) noexcept; + ImageDataPNG& operator=(ImageDataPNG&&) noexcept; + + const uint8_t* pixels() const override; + int getWidth() const override; + int getHeight() const override; + + /// returns getWidth() as PNGs don't support scaling + int getImageWidth() const override; + /// returns getHeight() as PNGs don't support scaling + int getImageHeight() const override; + +private: + const static unsigned int PNG_BYTES_TO_CHECK = 4; + std::vector imageData; + size_t x, y; +}; + +} // namespace jngl