Compare commits

...

1 Commits

Author SHA1 Message Date
d1a2a65c1b dump 2025-08-30 11:12:04 +02:00
18 changed files with 248 additions and 162 deletions

View File

@@ -4,7 +4,12 @@ project(sdk-top)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED YES)
if(NOT CMAKE_CROSSCOMPILING)
set(CBSDK_BACKEND_LIBRARY cbsdk_sfml)
endif ()
add_subdirectory(library)
if (NOT CMAKE_CROSSCOMPILING)
add_subdirectory(sfml-port)
add_subdirectory(examples)

View File

@@ -0,0 +1,34 @@
cmake_minimum_required(VERSION 3.10)
if (CMAKE_BUILD_TYPE STREQUAL "Debug")
# if (NOT DEFINED SANITIZE)
# set(SANITIZE YES)
# endif ()
endif ()
if (SANITIZE STREQUAL "YES")
message(STATUS "Enabling sanitizers!")
add_compile_options(-Werror -O0 -Wall -Wextra -pedantic -Wno-unused-parameter -Wno-unused-variable
-Wno-error=unused-function
-Wshadow -Wformat=2 -Wfloat-equal -D_GLIBCXX_DEBUG -Wconversion)
add_compile_options(-fsanitize=address -fno-sanitize-recover)
add_link_options(-fsanitize=address -fno-sanitize-recover)
endif ()
if (CMAKE_BUILD_TYPE STREQUAL "Release")
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE)
endif ()
if (NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
add_compile_options(-O3)
add_link_options(-O3)
endif ()
add_executable(main src/main.cpp)
set(CBSDK_BACKEND_INTERFACE cbsdk_sfml)
#target_include_directories(main PRIVATE include)
#target_include_directories(main PUBLIC include_public)
target_link_libraries(main PRIVATE SFML::Graphics)
target_link_libraries(main PUBLIC cbsdk)

View File

@@ -13,40 +13,40 @@ int main() {
EventLoop loop;
std::latch barrier{1};
SfmlSurface* surface_ptr;
SurfaceBase* surface_ptr;
int i = 0;
std::thread loop_thread{[&] {
SfmlSurface surface(&loop);
SurfaceBase surface(&loop);
surface_ptr = &surface;
barrier.count_down();
surface.set_window<GridWindow<SfmlSurface, 2, 2>>();
surface.set_window<GridWindow<SurfaceBase, 2, 2>>();
GridWindow<SfmlSurface, 2, 2>* window =
static_cast<GridWindow<SfmlSurface, 2, 2>*>(surface.get_window());
window->set_window<TextWindow<SubSurface<SfmlSurface>, std::string>>(0, 0, &loop, "hello");
window->set_window<TextWindow<SubSurface<SfmlSurface>, std::string>>(0, 1, &loop, "hello1");
window->set_window<GridWindow<SubSurface<SfmlSurface>, 2, 2>>(1, 0);
GridWindow<SubSurface<SfmlSurface>, 2, 2>* window2 =
static_cast<GridWindow<SubSurface<SfmlSurface>, 2, 2>*>(
GridWindow<SurfaceBase, 2, 2>* window =
static_cast<GridWindow<SurfaceBase, 2, 2>*>(surface.get_window());
window->set_window<TextWindow<SubSurface<SurfaceBase>, std::string>>(0, 0, &loop, "hello");
window->set_window<TextWindow<SubSurface<SurfaceBase>, std::string>>(0, 1, &loop, "hello1");
window->set_window<GridWindow<SubSurface<SurfaceBase>, 2, 2>>(1, 0);
GridWindow<SubSurface<SurfaceBase>, 2, 2>* window2 =
static_cast<GridWindow<SubSurface<SurfaceBase>, 2, 2>*>(
window->get_subsurface(1, 0).get_window());
window->set_window<TextWindow<SubSurface<SfmlSurface>, std::string>>(1, 1, &loop, "hello3");
window->set_window<TextWindow<SubSurface<SurfaceBase>, std::string>>(1, 1, &loop, "hello3");
window2->set_window<TextWindow<SubSurface<SfmlSurface>, std::string>>(
window2->set_window<TextWindow<SubSurface<SurfaceBase>, std::string>>(
0, 0, &loop, "hello2");
window2->set_window<TextWindow<SubSurface<SfmlSurface>, std::string>>(
window2->set_window<TextWindow<SubSurface<SurfaceBase>, std::string>>(
0, 1, &loop, "hello4");
window2->set_window<TextWindow<SubSurface<SfmlSurface>, std::string>>(
window2->set_window<TextWindow<SubSurface<SurfaceBase>, std::string>>(
1, 0, &loop, "hello5");
window2->set_window<TextWindow<SubSurface<SfmlSurface>, std::string>>(
window2->set_window<TextWindow<SubSurface<SurfaceBase>, std::string>>(
1, 1, &loop, "hello6");
loop.run([&] {
surface._sf_window.clear();
surface._texture.update(surface._image);
surface._sf_window.draw(surface._sprite);
surface._sf_window.display();
static_cast<TextWindow<SubSurface<SfmlSurface>, std::string>*>(
static_cast<TextWindow<SubSurface<SurfaceBase>, std::string>*>(
window->get_subsurface(0, 0).get_window())
->push(TextUpdateEvent<std::string>{std::string("Hello, SFML!") + std::to_string(i++)});
});

View File

@@ -1,5 +1,7 @@
cmake_minimum_required(VERSION 3.10)
add_subdirectory(backend_interface)
add_library(cbsdk
src/Window.cpp
include_public/Window.hpp
@@ -12,11 +14,18 @@ add_library(cbsdk
src/TextWindow.cpp
include_public/TextWindow.hpp
include_public/utils.hpp
include_public/SubSurface.hpp)
include_public/SubSurface.hpp
src/Surface.cpp)
target_include_directories(cbsdk PUBLIC include_public)
target_include_directories(cbsdk PRIVATE include)
if(NOT CBSDK_BACKEND_LIBRARY)
message(FATAL_ERROR "CBSDK_BACKEND_LIBRARY not set!")
endif ()
target_link_libraries(cbsdk PUBLIC cbsdk_backend_interface ${CBSDK_BACKEND_LIBRARY})
if (NOT CMAKE_CROSSCOMPILING)
add_subdirectory(test)
endif ()

View File

@@ -0,0 +1,10 @@
cmake_minimum_required(VERSION 3.10)
add_library(cbsdk_backend_interface INTERFACE
include_public/backend_interface.hpp)
target_include_directories(cbsdk_backend_interface INTERFACE include_public)
#if (NOT CMAKE_CROSSCOMPILING)
# add_subdirectory(test)
#endif ()

View File

@@ -0,0 +1,15 @@
//
// Created by stepus53 on 14.08.25.
//
#ifndef SDK_TOP_BACKEND_INTERFACE_HPP
#define SDK_TOP_BACKEND_INTERFACE_HPP
namespace SdkPort {
struct Pixel;
class SurfaceBase;
} // namespace SdkPort
#endif // SDK_TOP_BACKEND_INTERFACE_HPP

View File

@@ -15,6 +15,8 @@
#include <variant>
#include <functional>
#include "utils.hpp"
enum class EventHandlingResult { DONE, IGNORE, CONTINUE };
class Event {};
@@ -37,6 +39,8 @@ template<typename Derived, typename... T>
class EventHandler {
public:
EventHandler() { static_assert(HandlesAll<Derived, T...>); }
using HandledTypes = type_list<T...>;
};
class EventLoop;

View File

@@ -11,12 +11,10 @@
#include "Window.hpp"
#include "utils.hpp"
template<typename SurfaceType, unsigned nWidth, unsigned nHeight>
class GridWindow : public Window<SurfaceType> {
template<unsigned nWidth, unsigned nHeight>
class GridWindow : public Window {
public:
using PixelType = typename SurfaceType::PixelType;
explicit GridWindow(SurfaceType* owner) : Window<SurfaceType>(owner) {
explicit GridWindow(SubSurface* owner) : Window(owner) {
for (int i = 0; i < nWidth; ++i) {
for (int j = 0; j < nHeight; ++j) {
_grid[i][j].emplace(owner);
@@ -74,13 +72,9 @@ public:
_cell_height = this->_owner->get_height() / nHeight;
for (int i = 0; i < nWidth; ++i) {
for (int j = 0; j < nHeight; ++j) {
if constexpr (is_specialization_of<SubSurface, SurfaceType>::value) {
_grid[i][j]->set_pos(this->_owner->get_x_offset() + i * _cell_width + 1,
this->_owner->get_y_offset() + j * _cell_height + 1, _cell_width - 2,
_cell_height - 2);
} else {
_grid[i][j]->set_pos(i * _cell_width + 1, j * _cell_height + 1, _cell_width - 2, _cell_height - 2);
}
_grid[i][j]->set_pos(this->_owner->get_x_offset() + i * _cell_width + 1,
this->_owner->get_y_offset() + j * _cell_height + 1, _cell_width - 2,
_cell_height - 2);
}
}
refresh();
@@ -92,7 +86,7 @@ public:
_grid[x][y]->template set_window<WindowType>(std::forward<Args>(args)...);
}
SubSurface<SurfaceType>& get_subsurface(unsigned x, unsigned y) {
SubSurface& get_subsurface(unsigned x, unsigned y) {
// assert(x >= nWidth && y >= nHeight);
return *_grid[x][y];
}
@@ -101,21 +95,16 @@ public:
for (int i = 0; i < nWidth; ++i) {
for (int j = 0; j < nHeight; ++j) {
if (i == _current_focus_x && j == _current_focus_y) {
this->_owner->draw_rect(i * _cell_width, j * _cell_height, _cell_width, _cell_height,
PixelType(true));
this->_owner->draw_rect(i * _cell_width, j * _cell_height, _cell_width, _cell_height, Pixel(true));
} else {
this->_owner->draw_rect(i * _cell_width, j * _cell_height, _cell_width, _cell_height,
PixelType(false));
this->_owner->draw_rect(i * _cell_width, j * _cell_height, _cell_width, _cell_height, Pixel(false));
}
}
}
}
private:
using SubType = std::conditional_t<is_specialization_of<SubSurface, SurfaceType>::value, SurfaceType,
SubSurface<SurfaceType>>;
std::array<std::array<std::optional<SubType>, nWidth>, nHeight> _grid;
std::array<std::array<std::optional<SubSurface>, nWidth>, nHeight> _grid;
unsigned _cell_width = 0;
unsigned _cell_height = 0;

View File

@@ -5,17 +5,8 @@
#ifndef PIXEL_HPP
#define PIXEL_HPP
class Pixel {
};
#include "port_pixel.hpp"
struct BwPixel : public Pixel {
bool on = false;
BwPixel() = default;
BwPixel(bool on) : on(on) {}
bool operator==(const BwPixel& other) const { return on == other.on; }
bool operator!=(const BwPixel& other) const { return !(*this == other); }
};
using Pixel = SdkPort::Pixel;
#endif //PIXEL_HPP

View File

@@ -14,15 +14,12 @@
#include "Window.hpp"
#include "utils.hpp"
template<typename SurfaceParent>
class SubSurface : public Surface<SubSurface<SurfaceParent>, typename SurfaceParent::PixelType> {
class SubSurface : public StandardEventHandler<SubSurface> {
public:
using PixelType = typename SurfaceParent::PixelType;
SubSurface(Surface* parent) : _parent(parent) {}
SubSurface(SubSurface* parent) : _parent(parent->_parent) {}
SubSurface(SurfaceParent* parent) : _parent(parent) {}
SubSurface(SubSurface<SurfaceParent>* parent) : _parent(parent->_parent) {}
void draw_pixel_impl(unsigned x, unsigned y, const PixelType& pixel) {
void draw_pixel_impl(unsigned x, unsigned y, const Pixel& pixel) {
if (x >= _x_size || y >= _y_size) {
assert(false);
}
@@ -46,13 +43,33 @@ public:
this->handle(SurfaceResizeEvent(x_size, y_size));
}
template<typename T>
EventHandlingResult handle(const T& event) {
if (_window.get())
return _window->handle(event);
return EventHandlingResult::CONTINUE;
}
template<typename WindowType, typename... Args>
void set_window(Args&&... args) {
_window = std::make_unique<WindowType>(std::forward<Args>(args)...);
}
bool has_window() const { return _window != nullptr; }
Window* get_window() {
assert(has_window());
return _window.get();
}
private:
unsigned _x_offset = 0;
unsigned _y_offset = 0;
unsigned _x_size = 0;
unsigned _y_size = 0;
SurfaceParent* _parent;
Surface* _parent;
std::unique_ptr<Window> _window;
};
#endif // SUBSURFACE_HPP

View File

@@ -12,19 +12,17 @@
#include "Pixel.hpp"
#include "StandardEvents.hpp"
#include "Window.hpp"
#include "backend_interface.hpp"
#include "utils.hpp"
template<typename Derived, typename PixelType>
requires std::is_base_of_v<Pixel, PixelType>
class Surface : public StandardEventHandler<Derived> {
class SubSurface;
class Surface : public StandardEventHandler<Surface>, public SdkPort::SurfaceBase {
public:
Surface() { static_assert(std::is_same_v<PixelType, typename Derived::PixelType>); }
Surface();
~Surface();
void draw_pixel(unsigned x, unsigned y, const BwPixel& pixel) {
static_cast<Derived*>(this)->draw_pixel_impl(x, y, pixel);
}
void draw_rect(unsigned x, unsigned y, unsigned width, unsigned height, const BwPixel& pixel) {
void draw_rect(unsigned x, unsigned y, unsigned width, unsigned height, const Pixel& pixel) {
for (unsigned i = 0; i < width; ++i) {
draw_pixel(x + i, y, pixel);
draw_pixel(x + i, y + height - 1, pixel);
@@ -38,44 +36,17 @@ public:
void clear() {
for (unsigned x = 0; x < get_width(); x++) {
for (unsigned y = 0; y < get_height(); y++) {
draw_pixel(x, y, PixelType());
draw_pixel(x, y, Pixel());
}
}
}
int get_width() const { return static_cast<const Derived*>(this)->get_width_impl(); }
int get_height() const { return static_cast<const Derived*>(this)->get_height_impl(); }
template<typename T>
EventHandlingResult handle(const T& event) {
if (_window.get())
return _window->handle(event);
return EventHandlingResult::CONTINUE;
}
template<typename WindowType, typename... Args>
void set_window(Args&&... args) {
_window = std::make_unique<WindowType>(static_cast<Derived*>(this), std::forward<Args>(args)...);
}
Surface(const Surface& other) = delete;
Surface(Surface&& other) noexcept = delete;
Surface& operator=(const Surface& other) = delete;
Surface& operator=(Surface&& other) noexcept = delete;
bool has_window() const { return _window != nullptr; }
Window<Derived>* get_window() {
assert(has_window());
return _window.get();
}
EventHandlingResult handle(const T& event);
SubSurface& get_subsurface();
protected:
std::unique_ptr<Window<Derived>> _window = nullptr;
SubSurface* _subsurface = nullptr;
};
#endif // SURFACE_HPP

View File

@@ -10,18 +10,12 @@
#include "Event.hpp"
#include "Pixel.hpp"
#include "StandardEvents.hpp"
#include "SubSurface.hpp"
#include "utils.hpp"
template<typename Derived, typename PixelType>
requires std::is_base_of_v<Pixel, PixelType>
class Surface;
template<typename SurfaceType>
class Window : StandardEventHandler<Window<SurfaceType>> {
class Window : StandardEventHandler<Window> {
public:
using PixelType = typename SurfaceType::PixelType;
explicit Window(SurfaceType* owner) : _owner(owner) {
explicit Window(SubSurface* owner) : _owner(owner) {
// static_assert(is_specialization_of<Surface, SurfaceType>::value);
}
@@ -34,7 +28,7 @@ public:
virtual EventHandlingResult handle_v(SurfaceResizeEvent) { return EventHandlingResult::CONTINUE; }
protected:
SurfaceType* _owner = nullptr;
SubSurface* _owner = nullptr;
};
#endif // SURFACE_HPP

View File

@@ -5,10 +5,32 @@
#ifndef UTILS_HPP
#define UTILS_HPP
template <typename... Ts>
struct type_list {};
template <template <typename...> class T, typename U>
struct is_specialization_of: std::false_type {};
template <template <typename...> class T, typename... Us>
struct is_specialization_of<T, T<Us...>>: std::true_type {};
template <template <typename> class Target, typename List>
struct instantiate_all_with;
template <template <typename> class Target, template <typename...> class List, typename... Ts>
struct instantiate_all_with<Target, List<Ts...>> {
using type = std::tuple<Target<Ts>...>;
};
template <template <typename> class Func, typename List>
struct call_template_for_all;
template <template <typename> class Func, template <typename...> class List, typename... Ts>
struct call_template_for_all<Func, List<Ts...>> {
static void run() {
(Func<Ts>{}(), ...); // assumes Func<T>::operator() exists
}
};
#endif //UTILS_HPP

View File

@@ -0,0 +1,31 @@
//
// Created by stepus53 on 15.08.25.
//
#include "Surface.hpp"
#include "SubSurface.hpp"
Surface::Surface() : SurfaceBase() {}
Surface::~Surface() {
if (_subsurface)
delete _subsurface;
}
template<typename T>
EventHandlingResult Surface::handle(const T& event) {
if (_subsurface)
return _subsurface->handle(event);
return EventHandlingResult::CONTINUE;
}
SubSurface& Surface::get_subsurface() {
if (!_subsurface) {
_subsurface = new SubSurface(this);
}
return *_subsurface;
}
static_assert([]<typename... Ts>(type_list<Ts...>) {
((void) sizeof(&Surface::handle<Ts>), ...);
return true;
}(Surface::HandledTypes{}));

View File

@@ -9,35 +9,8 @@ FetchContent_Declare(SFML
SYSTEM)
FetchContent_MakeAvailable(SFML)
if (CMAKE_BUILD_TYPE STREQUAL "Debug")
# if (NOT DEFINED SANITIZE)
# set(SANITIZE YES)
# endif ()
endif ()
if (SANITIZE STREQUAL "YES")
message(STATUS "Enabling sanitizers!")
add_compile_options(-Werror -O0 -Wall -Wextra -pedantic -Wno-unused-parameter -Wno-unused-variable
-Wno-error=unused-function
-Wshadow -Wformat=2 -Wfloat-equal -D_GLIBCXX_DEBUG -Wconversion)
add_compile_options(-fsanitize=address -fno-sanitize-recover)
add_link_options(-fsanitize=address -fno-sanitize-recover)
endif ()
if (CMAKE_BUILD_TYPE STREQUAL "Release")
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE)
endif ()
if (NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
add_compile_options(-O3)
add_link_options(-O3)
endif ()
add_executable(main src/main.cpp
src/SfmlWindow.cpp
include_public/SfmlWindow.hpp)
target_include_directories(main PRIVATE include)
target_include_directories(main PUBLIC include_public)
target_link_libraries(main PRIVATE SFML::Graphics)
target_link_libraries(main PUBLIC cbsdk)
add_library(cbsdk_sfml src/SfmlWindow.cpp include_public/SfmlWindow.hpp
include_public/port_pixel.hpp)
target_include_directories(cbsdk_sfml PRIVATE include)
target_include_directories(cbsdk_sfml PUBLIC include_public)
target_link_libraries(cbsdk_sfml PRIVATE SFML::Graphics)

View File

@@ -5,31 +5,31 @@
#ifndef SFMLWINDOW_HPP
#define SFMLWINDOW_HPP
#include "Surface.hpp"
#include "Window.hpp"
#include <SFML/Graphics.hpp>
class SfmlSurface : public Surface<SfmlSurface, BwPixel>, public StandardEventQueue<SfmlSurface> {
namespace SdkPort {
struct Pixel;
class SurfaceBase {
public:
using PixelType = BwPixel;
SurfaceBase(EventLoop* loop);
SfmlSurface(EventLoop* loop);
~SurfaceBase(); // override;
~SfmlSurface(); // override;
void draw_pixel(unsigned x, unsigned y, const SdkPort::Pixel& pixel);
void draw_pixel_impl(unsigned x, unsigned y, const BwPixel& pixel);
unsigned get_width() const;
unsigned get_width_impl() const;
unsigned get_height() const;
unsigned get_height_impl() const;
template<typename T>
EventHandlingResult handle(const T& event) {
return _window->handle(event);
}
SurfaceBase(const SurfaceBase& other) = delete;
EventHandlingResult handle(SurfaceResizeEvent event);
SurfaceBase(SurfaceBase&& other) noexcept = delete;
SurfaceBase& operator=(const SurfaceBase& other) = delete;
SurfaceBase& operator=(SurfaceBase&& other) noexcept = delete;
sf::RenderWindow _sf_window;
@@ -37,5 +37,6 @@ public:
sf::Texture _texture;
sf::Sprite _sprite;
};
}
#endif // SFMLWINDOW_HPP

View File

@@ -0,0 +1,20 @@
//
// Created by stepus53 on 15.08.25.
//
#ifndef SDK_TOP_PORT_PIXEL_HPP
#define SDK_TOP_PORT_PIXEL_HPP
namespace SdkPort {
struct Pixel {
bool on = false;
Pixel() = default;
Pixel(bool on) : on(on) {}
bool operator==(const Pixel& other) const { return on == other.on; }
bool operator!=(const Pixel& other) const { return !(*this == other); }
};
} // namespace SdkPort
#endif // SDK_TOP_PORT_PIXEL_HPP

View File

@@ -4,15 +4,15 @@
#include "SfmlWindow.hpp"
void SfmlSurface::draw_pixel_impl(unsigned x, unsigned y, const BwPixel& pixel) {
void SurfaceBase::draw_pixel_impl(unsigned x, unsigned y, const BwPixel& pixel) {
_image.setPixel({x, y}, pixel.on ? sf::Color::Black : sf::Color::White);
}
unsigned SfmlSurface::get_width_impl() const { return _image.getSize().x; }
unsigned SurfaceBase::get_width_impl() const { return _image.getSize().x; }
unsigned SfmlSurface::get_height_impl() const { return _image.getSize().y; }
unsigned SurfaceBase::get_height_impl() const { return _image.getSize().y; }
EventHandlingResult SfmlSurface::handle(SurfaceResizeEvent event) {
EventHandlingResult SurfaceBase::handle(SurfaceResizeEvent event) {
_sf_window.clear();
_image.resize({event.width, event.height});
_texture.resize({event.width, event.height});
@@ -23,9 +23,9 @@ EventHandlingResult SfmlSurface::handle(SurfaceResizeEvent event) {
return _window->handle(event);
}
SfmlSurface::SfmlSurface(EventLoop* loop) :
Surface<SfmlSurface, BwPixel>(),
EventQueue<SfmlSurface, KeyboardEvent, SurfaceEvent, SurfaceResizeEvent>(loop, this),
SurfaceBase::SurfaceBase(EventLoop* loop) :
Surface<SurfaceBase, BwPixel>(),
EventQueue<SurfaceBase, KeyboardEvent, SurfaceEvent, SurfaceResizeEvent>(loop, this),
_sf_window(sf::VideoMode({640, 480}), "Test"), _image({640, 480}, sf::Color::White), _texture(_image),
_sprite(_texture) {
_sf_window.setFramerateLimit(60);
@@ -34,4 +34,4 @@ SfmlSurface::SfmlSurface(EventLoop* loop) :
_sf_window.display();
}
SfmlSurface::~SfmlSurface() {}
SurfaceBase::~SurfaceBase() {}