mirror of
https://github.com/usatiuk/cardboy.git
synced 2025-10-28 23:27:49 +01:00
135 lines
3.7 KiB
C++
135 lines
3.7 KiB
C++
#pragma once
|
|
|
|
#include "input_state.hpp"
|
|
|
|
#include <concepts>
|
|
#include <cstdint>
|
|
|
|
namespace cardboy::sdk {
|
|
|
|
namespace detail {
|
|
template<typename Impl>
|
|
concept HasClearImpl = requires(Impl& impl, bool value) {
|
|
{ impl.clear_impl(value) };
|
|
};
|
|
|
|
template<typename Impl>
|
|
concept HasFrameReadyImpl = requires(Impl& impl) {
|
|
{ impl.frameReady_impl() };
|
|
};
|
|
|
|
template<typename Impl>
|
|
concept HasSendFrameImpl = requires(Impl& impl, bool flag) {
|
|
{ impl.sendFrame_impl(flag) };
|
|
};
|
|
|
|
template<typename Impl>
|
|
concept HasDrawBits8Impl = requires(Impl& impl, int x, int y, std::uint8_t bits) { impl.drawBits8_impl(x, y, bits); };
|
|
|
|
template<typename Impl>
|
|
concept HasFrameInFlightImpl = requires(const Impl& impl) {
|
|
{ impl.frameInFlight_impl() } -> std::convertible_to<bool>;
|
|
};
|
|
|
|
template<typename Impl>
|
|
concept HasSleepMsImpl = requires(Impl& impl, std::uint32_t value) {
|
|
{ impl.sleep_ms_impl(value) };
|
|
};
|
|
} // namespace detail
|
|
|
|
template<typename Impl>
|
|
class FramebufferFacade {
|
|
public:
|
|
[[nodiscard]] __attribute__((always_inline)) int width() const { return impl().width_impl(); }
|
|
[[nodiscard]] __attribute__((always_inline)) int height() const { return impl().height_impl(); }
|
|
|
|
__attribute__((always_inline)) void drawPixel(int x, int y, bool on) { impl().drawPixel_impl(x, y, on); }
|
|
|
|
__attribute__((always_inline)) void drawBits8(int x, int y, std::uint8_t bits) {
|
|
if constexpr (detail::HasDrawBits8Impl<Impl>) {
|
|
impl().drawBits8_impl(x, y, bits);
|
|
} else {
|
|
defaultDrawBits8(x, y, bits);
|
|
}
|
|
}
|
|
|
|
void clear(bool on) {
|
|
if constexpr (detail::HasClearImpl<Impl>) {
|
|
impl().clear_impl(on);
|
|
} else {
|
|
defaultClear(on);
|
|
}
|
|
}
|
|
|
|
__attribute__((always_inline)) void frameReady() {
|
|
if constexpr (detail::HasFrameReadyImpl<Impl>)
|
|
impl().frameReady_impl();
|
|
}
|
|
|
|
__attribute__((always_inline)) void sendFrame(bool clearDrawBuffer = true) {
|
|
if constexpr (detail::HasSendFrameImpl<Impl>)
|
|
impl().sendFrame_impl(clearDrawBuffer);
|
|
}
|
|
|
|
[[nodiscard]] __attribute__((always_inline)) bool isFrameInFlight() const {
|
|
if constexpr (detail::HasFrameInFlightImpl<Impl>)
|
|
return impl().frameInFlight_impl();
|
|
return false;
|
|
}
|
|
|
|
protected:
|
|
FramebufferFacade() = default;
|
|
~FramebufferFacade() = default;
|
|
|
|
private:
|
|
[[nodiscard]] __attribute__((always_inline)) Impl& impl() { return static_cast<Impl&>(*this); }
|
|
[[nodiscard]] __attribute__((always_inline)) const Impl& impl() const { return static_cast<const Impl&>(*this); }
|
|
|
|
void defaultClear(bool on) {
|
|
for (int y = 0; y < height(); ++y)
|
|
for (int x = 0; x < width(); ++x)
|
|
drawPixel(x, y, on);
|
|
}
|
|
|
|
void defaultDrawBits8(int x, int y, std::uint8_t bits) {
|
|
for (int col = 0; col < 8; ++col) {
|
|
const std::uint8_t mask = static_cast<std::uint8_t>(1u << (7 - col));
|
|
const bool pixelOn = (bits & mask) != 0;
|
|
drawPixel(x + col, y, pixelOn);
|
|
}
|
|
}
|
|
};
|
|
|
|
template<typename Impl>
|
|
class InputFacade {
|
|
public:
|
|
InputState readState() { return impl().readState_impl(); }
|
|
|
|
protected:
|
|
InputFacade() = default;
|
|
~InputFacade() = default;
|
|
|
|
private:
|
|
[[nodiscard]] Impl& impl() { return static_cast<Impl&>(*this); }
|
|
};
|
|
|
|
template<typename Impl>
|
|
class ClockFacade {
|
|
public:
|
|
std::uint32_t millis() { return impl().millis_impl(); }
|
|
|
|
void sleep_ms(std::uint32_t ms) {
|
|
if constexpr (detail::HasSleepMsImpl<Impl>)
|
|
impl().sleep_ms_impl(ms);
|
|
}
|
|
|
|
protected:
|
|
ClockFacade() = default;
|
|
~ClockFacade() = default;
|
|
|
|
private:
|
|
[[nodiscard]] Impl& impl() { return static_cast<Impl&>(*this); }
|
|
};
|
|
|
|
} // namespace cardboy::sdk
|