Compare commits

...

3 Commits

Author SHA1 Message Date
ab32731f4d dump 2025-07-28 09:39:13 +02:00
474a0b2a43 buttons interrupt 2025-07-26 15:34:20 +02:00
35219c353c set correct charge termination current 2025-07-26 12:43:20 +02:00
40 changed files with 6214 additions and 241 deletions

1
Firmware/.gitignore vendored
View File

@@ -1,3 +1,4 @@
build
cmake-build*
.idea
.cache

23
Firmware/.vscode/c_cpp_properties.json vendored Normal file
View File

@@ -0,0 +1,23 @@
{
"configurations": [
{
"name": "ESP-IDF",
"compilerPath": "${config:idf.toolsPath}/tools/riscv32-esp-elf/esp-14.2.0_20241119/riscv32-esp-elf/bin/riscv32-esp-elf-gcc",
"compileCommands": "${config:idf.buildPath}/compile_commands.json",
"includePath": [
"${config:idf.espIdfPath}/components/**",
"${config:idf.espIdfPathWin}/components/**",
"${workspaceFolder}/**"
],
"browse": {
"path": [
"${config:idf.espIdfPath}/components",
"${config:idf.espIdfPathWin}/components",
"${workspaceFolder}"
],
"limitSymbolsToIncludedHeaders": true
}
}
],
"version": 4
}

15
Firmware/.vscode/launch.json vendored Normal file
View File

@@ -0,0 +1,15 @@
{
"version": "0.2.0",
"configurations": [
{
"type": "gdbtarget",
"request": "attach",
"name": "Eclipse CDT GDB Adapter"
},
{
"type": "espidf",
"name": "Launch",
"request": "launch"
}
]
}

4
Firmware/.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,4 @@
{
"idf.flashType": "JTAG",
"idf.port": "/dev/tty.usbmodem12401"
}

View File

@@ -0,0 +1,5 @@
idf_component_register()
add_subdirectory("${CMAKE_CURRENT_LIST_DIR}/../../sdk" cb-sdk-build)
target_link_libraries(${COMPONENT_LIB} INTERFACE cbsdk)

View File

@@ -9,5 +9,5 @@ idf_component_register(SRCS
src/shutdowner.cpp
src/buttons.cpp
src/power_helper.cpp
PRIV_REQUIRES spi_flash esp_driver_i2c driver
PRIV_REQUIRES spi_flash esp_driver_i2c driver sdk-esp
INCLUDE_DIRS "include")

View File

@@ -24,12 +24,14 @@ public:
static Buttons& get();
void pooler(); // FIXME:
uint8_t get_pressed();
void install_isr();
TaskHandle_t _pooler_task;
private:
Buttons();
volatile uint8_t _current;
TaskHandle_t _pooler_task;
};

View File

@@ -11,10 +11,30 @@ class DispTools {
public:
static DispTools& get();
void clear();
bool get_pixel(int x, int y);
void set_pixel(int x, int y);
void reset_pixel(int x, int y);
void clear() {
for (int y = 0; y < DISP_HEIGHT; y++) {
for (int x = 0; x < DISP_WIDTH; x++) {
disp_frame[y][x] = 1;
}
}
}
bool get_pixel(int x, int y) {
// if (x < 0 || x >= DISP_WIDTH || y < 0 || y >= DISP_HEIGHT)
// assert(false);
return disp_frame[y][x];
}
void reset_pixel(int x, int y) {
// if (x < 0 || x >= DISP_WIDTH || y < 0 || y >= DISP_HEIGHT)
// assert(false);
disp_frame[y][x] = true;
}
void set_pixel(int x, int y) {
// if (x < 0 || x >= DISP_WIDTH || y < 0 || y >= DISP_HEIGHT)
// assert(false);
//
disp_frame[y][x] = false;
}
void draw_rectangle(int x1, int y1, int x2, int y2);
void draw_line(int x1, int y1, int x2, int y2);
void draw_circle(int x, int y, int r);

View File

@@ -12,6 +12,9 @@
#include <array>
#include <bitset>
#include "Surface.hpp"
#include "Window.hpp"
class SMD {
public:
using disp_line_t = std::bitset<400>;
@@ -41,4 +44,27 @@ private:
std::array<uint8_t, kLineBytes> prep_line(const SMD::disp_line_t& line);
};
class SMDSurface : public Surface<SMDSurface, BwPixel>, public StandardEventQueue<SMDSurface> {
public:
SMDSurface(EventLoop* loop);
~SMDSurface() override;
void draw_pixel_impl(unsigned x, unsigned y, const BwPixel& pixel);
void clear_impl();
int get_width_impl() const;
int get_height_impl() const;
template<typename T>
EventHandlingResult handle(const T& event) {
return _window->handle(event);
}
EventHandlingResult handle(SurfaceResizeEvent event);
};
#endif // DISPLAY_HPP

View File

@@ -11,10 +11,10 @@ class PowerHelper {
public:
static PowerHelper& get();
bool is_slow() const;
void set_slow(bool slow);
void reset_slow_isr(); // FIXME:
void delay(int slow_ms, int normal_ms);
bool is_slow() const;
void set_slow(bool slow);
BaseType_t reset_slow_isr(BaseType_t* xHigherPriorityTaskWoken);
void delay(int slow_ms, int normal_ms);
void install_isr();

View File

@@ -61,7 +61,7 @@ constexpr uint16_t voltageToReg(float voltage) {
return static_cast<uint16_t>(voltage / (0.078125f * 0.001f)); // Convert to register value
}
static constexpr uint16_t DesignCap = mahToCap(DesignCapMah);
static constexpr uint16_t IchgTerm = currentToReg(25);
static constexpr uint16_t IchgTerm = currentToReg(10);
static constexpr uint16_t VEmpty = 0b1001011001100001; // (3V/3.88V)
static constexpr uint16_t dQAcc = (DesignCap / 32);

View File

@@ -29,6 +29,28 @@ Buttons& Buttons::get() {
static void start_pooler(void* arg) { static_cast<Buttons*>(arg)->pooler(); }
static bool is_on_low;
static void wakeup(void* arg) {
if (is_on_low) {
ESP_ERROR_CHECK(gpio_set_intr_type(EXP_INT, GPIO_INTR_HIGH_LEVEL));
ESP_ERROR_CHECK(gpio_wakeup_enable(EXP_INT, GPIO_INTR_HIGH_LEVEL));
is_on_low = false;
BaseType_t xResult = pdFAIL;
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
xTaskNotifyFromISR(Buttons::get()._pooler_task, 0, eNoAction, &xHigherPriorityTaskWoken);
PowerHelper::get().reset_slow_isr(&xHigherPriorityTaskWoken);
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
} else {
ESP_ERROR_CHECK(gpio_set_intr_type(EXP_INT, GPIO_INTR_LOW_LEVEL));
ESP_ERROR_CHECK(gpio_wakeup_enable(EXP_INT, GPIO_INTR_LOW_LEVEL));
is_on_low = true;
}
}
Buttons::Buttons() {
ESP_ERROR_CHECK(i2c_master_bus_add_device(I2cGlobal::get().get_bus_handle(), &dev_cfg, &dev_handle));
uint8_t buf2[2];
@@ -38,10 +60,17 @@ Buttons::Buttons() {
buf2[1] = 0xFF;
ESP_ERROR_CHECK(i2c_master_transmit(dev_handle, buf2, sizeof(buf2), -1));
buf2[0] = 7;
buf2[1] = 0x80;
ESP_ERROR_CHECK(i2c_master_transmit(dev_handle, buf2, sizeof(buf2), -1));
xTaskCreate(&start_pooler, "ButtonsPooler", 2048, this, 1, &_pooler_task);
}
ESP_ERROR_CHECK(gpio_reset_pin(EXP_INT));
ESP_ERROR_CHECK(gpio_set_direction(EXP_INT, GPIO_MODE_INPUT));
ESP_ERROR_CHECK(gpio_set_pull_mode(EXP_INT, GPIO_FLOATING));
ESP_ERROR_CHECK(gpio_set_intr_type(EXP_INT, GPIO_INTR_LOW_LEVEL));
ESP_ERROR_CHECK(gpio_wakeup_enable(EXP_INT, GPIO_INTR_LOW_LEVEL));
is_on_low = true;
}
static void delay(unsigned long long loop) {
for (unsigned long long i = 0; i < loop; i++) {
@@ -51,12 +80,17 @@ static void delay(unsigned long long loop) {
void Buttons::pooler() {
while (true) {
uint8_t reg = 0;
uint8_t buffer;
BaseType_t xResult = xTaskNotifyWait(pdFALSE, ULONG_MAX, nullptr, portMAX_DELAY);
uint8_t reg = 0;
uint8_t buffer;
ESP_ERROR_CHECK(
i2c_master_transmit_receive(dev_handle, &reg, sizeof(reg), reinterpret_cast<uint8_t*>(&buffer), 1, -1));
_current = buffer;
PowerHelper::get().delay(10000, 200);
// read second port too to clear the interrupt
reg = 1;
ESP_ERROR_CHECK(
i2c_master_transmit_receive(dev_handle, &reg, sizeof(reg), reinterpret_cast<uint8_t*>(&buffer), 1, -1));
}
}
uint8_t Buttons::get_pressed() { return _current; }
void Buttons::install_isr() { gpio_isr_handler_add(EXP_INT, wakeup, nullptr); }

View File

@@ -13,16 +13,6 @@ DispTools& DispTools::get() {
return disp_tools;
}
void DispTools::clear() {
for (int y = 0; y < DISP_HEIGHT; y++) {
for (int x = 0; x < DISP_WIDTH; x++) {
disp_frame[y][x] = 1;
}
}
}
bool DispTools::get_pixel(int x, int y) { return disp_frame[y][x]; }
void DispTools::reset_pixel(int x, int y) { disp_frame[y][x] = true; }
void DispTools::set_pixel(int x, int y) { disp_frame[y][x] = false; }
void DispTools::draw_rectangle(int x1, int y1, int x2, int y2) {
int dy = y2 - y1;
while (std::abs(dy) > 0) {

View File

@@ -9,6 +9,8 @@
#include <driver/gpio.h>
#include "driver/spi_master.h"
#include "disp_tools.hpp"
// This solution is attributed to Rich Schroeppel in the Programming Hacks section
// TODO: Why does the device flag not work?
unsigned char reverse_bits3(unsigned char b) { return (b * 0x0202020202ULL & 0x010884422010ULL) % 0x3ff; }
@@ -68,3 +70,24 @@ void SMD::draw(const disp_frame_t& frame) {
ESP_ERROR_CHECK(spi_device_transmit(_spi, &t));
}
}
void SMDSurface::draw_pixel_impl(unsigned x, unsigned y, const BwPixel& pixel) {
if (pixel.on)
DispTools::get().set_pixel(x, y);
else
DispTools::get().reset_pixel(x, y);
}
void SMDSurface::clear_impl() { DispTools::get().clear(); }
int SMDSurface::get_width_impl() const { return DISP_WIDTH; }
int SMDSurface::get_height_impl() const { return DISP_HEIGHT; }
EventHandlingResult SMDSurface::handle(SurfaceResizeEvent event) { return _window->handle(event); }
SMDSurface::SMDSurface(EventLoop* loop) :
Surface<SMDSurface, BwPixel>(),
EventQueue<SMDSurface, KeyboardEvent, SurfaceEvent, SurfaceResizeEvent>(loop, this) {}
SMDSurface::~SMDSurface() {}

View File

@@ -33,13 +33,19 @@
#include <spi_global.hpp>
#include <string>
#include <thread>
#include "GridWindow.hpp"
#include "TextWindow.hpp"
#include "display.hpp"
FbTty tty;
extern "C" void app_main() {
esp_pm_config_t pm_config = {
.max_freq_mhz = CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ, .min_freq_mhz = 16, .light_sleep_enable = true};
// ESP_ERROR_CHECK(esp_pm_configure(&pm_config));
ESP_ERROR_CHECK(esp_pm_configure(&pm_config));
printf("Hello world!\n");
// TODO: Where to put that?
ESP_ERROR_CHECK(esp_sleep_enable_gpio_wakeup());
@@ -47,17 +53,18 @@ extern "C" void app_main() {
// ESP_ERROR_CHECK(gpio_install_isr_service(0));
PowerHelper::get();
Shutdowner::get();
Buttons::get();
ESP_ERROR_CHECK(gpio_install_isr_service(0));
Shutdowner::get().install_isr();
PowerHelper::get().install_isr();
Buttons::get();
Buttons::get().install_isr();
I2cGlobal::get();
BatMon::get();
SpiGlobal::get();
SMD::get();
SMD::get().clear();
DispTools::get().clear();
DispTools::get().draw_line(0, 0, 400, 240);
DispTools::get().draw_line(0, 0, 399, 239);
DispTools::get().draw_circle(100, 100, 20);
DispTools::get().draw_to_display();
tty.putstr("Hello\nworld!");
@@ -67,21 +74,59 @@ extern "C" void app_main() {
int lastmove = 0;
EventLoop loop;
SMDSurface surface(&loop);
surface.set_window<GridWindow<SMDSurface, BwPixel, 2, 2>>();
GridWindow<SMDSurface, BwPixel, 2, 2>* window =
static_cast<GridWindow<SMDSurface, BwPixel, 2, 2>*>(surface.get_window());
window->set_window<TextWindow<SubSurface<SMDSurface, BwPixel>, BwPixel, std::string>>(0, 0, &loop, "hello");
window->set_window<TextWindow<SubSurface<SMDSurface, BwPixel>, BwPixel, std::string>>(0, 1, &loop, "hello1");
window->set_window<GridWindow<SubSurface<SMDSurface, BwPixel>, BwPixel, 2, 2>>(1, 0);
GridWindow<SubSurface<SMDSurface, BwPixel>, BwPixel, 2, 2>* window2 =
static_cast<GridWindow<SubSurface<SMDSurface, BwPixel>, BwPixel, 2, 2>*>(
window->get_subsurface(1, 0).get_window());
window->set_window<TextWindow<SubSurface<SMDSurface, BwPixel>, BwPixel, std::string>>(1, 1, &loop, "hello3");
window2->set_window<TextWindow<SubSurface<SubSurface<SMDSurface, BwPixel>, BwPixel>, BwPixel, std::string>>(
0, 0, &loop, "hello2");
window2->set_window<TextWindow<SubSurface<SubSurface<SMDSurface, BwPixel>, BwPixel>, BwPixel, std::string>>(
0, 1, &loop, "hello4");
window2->set_window<TextWindow<SubSurface<SubSurface<SMDSurface, BwPixel>, BwPixel>, BwPixel, std::string>>(
1, 0, &loop, "hello5");
window2->set_window<TextWindow<SubSurface<SubSurface<SMDSurface, BwPixel>, BwPixel>, BwPixel, std::string>>(
1, 1, &loop, "hello6");
auto* tl_text = static_cast<TextWindow<SubSurface<SMDSurface, BwPixel>, BwPixel, std::string>*>(
window->get_subsurface(0, 0).get_window());
surface.handle(SurfaceResizeEvent{DISP_WIDTH, DISP_HEIGHT});
std::thread loop_thread{[&] { loop.run([&] { DispTools::get().draw_to_display(); }); }};
uint8_t old_pressed = 0;
while (true) {
// SMD::clear();
// printf("Voltage: %f\n", BatMon::get_voltage());
DispTools::get().clear();
tty.reset();
// DispTools::get().clear();
// tty.reset();
uint8_t pressed = Buttons::get().get_pressed();
if (pressed & BTN_LEFT)
rx -= 5;
if (pressed & BTN_DOWN)
ry += 5;
if (pressed & BTN_UP)
ry -= 5;
if (pressed & BTN_RIGHT)
rx += 5;
if ((pressed & BTN_LEFT) && !(old_pressed & BTN_LEFT))
surface.push(KeyboardEvent{Key::Left});
if ((pressed & BTN_DOWN) && !(old_pressed & BTN_DOWN))
surface.push(KeyboardEvent{Key::Down});
if ((pressed & BTN_UP) && !(old_pressed & BTN_UP))
surface.push(KeyboardEvent{Key::Up});
if ((pressed & BTN_RIGHT) && !(old_pressed & BTN_RIGHT))
surface.push(KeyboardEvent{Key::Right});
if ((pressed & BTN_SELECT) && !(old_pressed & BTN_SELECT))
surface.push(KeyboardEvent{Key::Escape});
if ((pressed & BTN_START) && !(old_pressed & BTN_START))
surface.push(KeyboardEvent{Key::Enter});
old_pressed = pressed;
if (pressed == 0 && !PowerHelper::get().is_slow())
lastmove++;
@@ -96,26 +141,26 @@ extern "C" void app_main() {
}
bool slow = PowerHelper::get().is_slow();
tty.fmt("{:.1f}mA {:.1f}V {:.1f}mAh {}", BatMon::get().get_current(), BatMon::get().get_voltage(),
BatMon::get().get_charge(), slow ? "S" : "");
tl_text->push(TextUpdateEvent<std::string>(std::format("{:.1f}mA {:.1f}V {:.1f}mAh {}\n Buttons: {:08b}",
BatMon::get().get_current(), BatMon::get().get_voltage(),
BatMon::get().get_charge(), slow ? "S" : "", pressed)));
tty.fmt("Buttons: {:08b}", pressed);
if (rx < 30)
rx = 30;
if (rx > 370)
rx = 370;
if (ry < 30)
ry = 30;
if (ry > 210)
ry = 210;
// tty.fmt("Button: {}", pressed);
DispTools::get().draw_circle(rx, ry, 20);
// printf("Restarting in %d seconds...\n", i);
DispTools::get().draw_to_display();
// if (rx < 30)
// rx = 30;
// if (rx > 370)
// rx = 370;
// if (ry < 30)
// ry = 30;
// if (ry > 210)
// ry = 210;
// // tty.fmt("Button: {}", pressed);
// DispTools::get().draw_circle(rx, ry, 20);
// // printf("Restarting in %d seconds...\n", i);
// DispTools::get().draw_to_display();
PowerHelper::get().delay(10000, 30);
}
// printf("Restarting now.\n");
// fflush(stdout);
// esp_restart();
loop_thread.join();
}

View File

@@ -17,7 +17,6 @@ PowerHelper& PowerHelper::get() {
}
bool PowerHelper::is_slow() const { return _slow; }
void PowerHelper::set_slow(bool slow) {
return;
_slow = slow;
if (_slow) {
xEventGroupClearBits(_event_group, 1);
@@ -26,32 +25,22 @@ void PowerHelper::set_slow(bool slow) {
}
}
void PowerHelper::reset_slow_isr() {
BaseType_t PowerHelper::reset_slow_isr(BaseType_t* xHigherPriorityTaskWoken) {
_slow = false;
return xEventGroupSetBitsFromISR(_event_group, 1, xHigherPriorityTaskWoken);
}
static void wakeup(void* arg) {
BaseType_t xHigherPriorityTaskWoken, xResult;
xHigherPriorityTaskWoken = pdFALSE;
_slow = false;
xResult = xEventGroupSetBitsFromISR(_event_group, 1, &xHigherPriorityTaskWoken);
xResult = static_cast<PowerHelper*>(arg)->reset_slow_isr(&xHigherPriorityTaskWoken);
if (xResult != pdFAIL) {
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
};
}
static void wakeup(void* arg) { static_cast<PowerHelper*>(arg)->reset_slow_isr(); }
PowerHelper::PowerHelper() : _event_group(xEventGroupCreate()) {
// ESP_ERROR_CHECK(gpio_reset_pin(EXP_INT));
// ESP_ERROR_CHECK(gpio_set_direction(EXP_INT, GPIO_MODE_INPUT));
// ESP_ERROR_CHECK(gpio_set_pull_mode(EXP_INT, GPIO_FLOATING));
// ESP_ERROR_CHECK(gpio_set_intr_type(EXP_INT, GPIO_INTR_HIGH_LEVEL));
// ESP_ERROR_CHECK(gpio_wakeup_enable(EXP_INT, GPIO_INTR_HIGH_LEVEL));
// ESP_ERROR_CHECK(gpio_install_isr_service(0));
// gpio_isr_handler_add(EXP_INT, wakeup, this);
set_slow(false);
}
PowerHelper::PowerHelper() : _event_group(xEventGroupCreate()) { set_slow(false); }
void PowerHelper::delay(int slow_ms, int normal_ms) {
if (is_slow()) {

View File

@@ -0,0 +1,76 @@
# Generated from CLion C/C++ Code Style settings
---
Language: Cpp
BasedOnStyle: LLVM
AccessModifierOffset: -4
AlignAfterOpenBracket: Align
AlignConsecutiveAssignments:
Enabled: true
AcrossEmptyLines: false
AcrossComments: false
AlignConsecutiveBitFields:
Enabled: true
AcrossEmptyLines: false
AcrossComments: false
AlignConsecutiveDeclarations:
Enabled: true
AcrossEmptyLines: false
AcrossComments: false
AlignConsecutiveMacros:
Enabled: true
AcrossEmptyLines: false
AcrossComments: false
AlignTrailingComments:
Kind: Always
OverEmptyLines: 2
SpacesBeforeTrailingComments: 1
AlignOperands: Align
AlignEscapedNewlines: Right
AlwaysBreakTemplateDeclarations: Yes
BraceWrapping:
AfterCaseLabel: false
AfterClass: false
AfterControlStatement: false
AfterEnum: false
AfterFunction: false
AfterNamespace: false
AfterStruct: false
AfterUnion: false
AfterExternBlock: false
BeforeCatch: false
BeforeElse: false
BeforeLambdaBody: false
BeforeWhile: false
SplitEmptyFunction: true
SplitEmptyRecord: true
SplitEmptyNamespace: true
BreakBeforeBraces: Custom
BreakConstructorInitializers: AfterColon
BreakConstructorInitializersBeforeComma: false
ColumnLimit: 120
ConstructorInitializerAllOnOneLineOrOnePerLine: false
ContinuationIndentWidth: 8
IncludeCategories:
- Regex: '^<.*'
Priority: 1
- Regex: '^".*'
Priority: 2
- Regex: '.*'
Priority: 3
IncludeIsMainRegex: '([-_](test|unittest))?$'
IndentCaseLabels: true
IndentWidth: 4
InsertNewlineAtEOF: true
MacroBlockBegin: ''
MacroBlockEnd: ''
MaxEmptyLinesToKeep: 2
PointerAlignment: Left
SpaceAfterCStyleCast: true
SpaceAfterTemplateKeyword: false
SpaceBeforeRangeBasedForLoopColon: false
SpaceInEmptyParentheses: false
SpacesInAngles: false
SpacesInConditionalStatement: false
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
...

View File

@@ -0,0 +1,11 @@
cmake_minimum_required(VERSION 3.10)
project(sdk-top)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED YES)
add_subdirectory(library)
if (NOT CMAKE_CROSSCOMPILING)
add_subdirectory(sfml-port)
add_subdirectory(examples)
endif ()

View File

View File

@@ -0,0 +1,22 @@
cmake_minimum_required(VERSION 3.10)
add_library(cbsdk
src/Window.cpp
include_public/Window.hpp
include_public/Pixel.hpp
src/Event.cpp
include_public/Event.hpp
include_public/StandardEvents.hpp
include_public/Surface.hpp
include_public/Fonts.hpp
src/TextWindow.cpp
include_public/TextWindow.hpp
include_public/utils.hpp
include_public/SubSurface.hpp)
target_include_directories(cbsdk PUBLIC include_public)
target_include_directories(cbsdk PRIVATE include)
if (NOT CMAKE_CROSSCOMPILING)
add_subdirectory(test)
endif ()

View File

@@ -0,0 +1,137 @@
//
// Created by Stepan Usatiuk on 26.07.2025.
//
#ifndef EVENT_HPP
#define EVENT_HPP
#include <algorithm>
#include <concepts>
#include <condition_variable>
#include <list>
#include <mutex>
#include <optional>
#include <type_traits>
enum class EventHandlingResult { DONE, IGNORE, CONTINUE };
class Event {};
struct LoopQuitEvent : public Event {};
template<typename T>
concept IsEvent = std::is_base_of_v<Event, T>;
template<typename H, typename E>
concept HasHandleFor = requires(H h, E e) {
{ h.handle(e) } -> std::same_as<EventHandlingResult>;
};
template<typename H, typename... Ts>
concept HandlesAll = (HasHandleFor<H, Ts> && ...);
template<typename Derived, typename... T>
requires(IsEvent<T> && ...)
class EventHandler {
public:
EventHandler() { static_assert(HandlesAll<Derived, T...>); }
};
class EventLoop;
class EventQueueBase {
public:
virtual void process_events() = 0;
virtual ~EventQueueBase() = default;
};
template<typename HandlerType, typename... Ts>
class EventQueue : public EventQueueBase {
public:
EventQueue(EventLoop* loop, HandlerType* handler) : _loop(loop), _handler(handler) {};
std::optional<std::variant<Ts...>> poll();
void process_events() override {
while (auto event = poll()) {
std::visit([this](auto&& e) { _handler->handle(e); }, *event);
}
}
template<typename T>
requires std::disjunction_v<std::is_same<T, Ts>...>
void push(T&& event);
private:
EventLoop* _loop;
HandlerType* _handler;
std::list<std::variant<Ts...>> _events;
};
class EventLoop : EventHandler<EventLoop, LoopQuitEvent>, public EventQueue<EventLoop, LoopQuitEvent> {
public:
EventLoop() : EventQueue<EventLoop, LoopQuitEvent>(this, this) {}
template<typename... Ts>
void notify_pending(EventQueue<Ts...>* queue) {
std::lock_guard<std::mutex> lock(_mutex);
// TODO:
if (std::find(_events.begin(), _events.end(), queue) != _events.end()) {
return; // Already registered
}
_events.push_back(queue);
_condition.notify_all();
}
void run(std::function<void()> after_callback) {
while (_running) {
std::list<EventQueueBase*> new_events;
{
std::unique_lock<std::mutex> lock(_mutex);
_condition.wait(lock, [this] { return !_events.empty() || !_running; });
std::swap(new_events, _events);
}
for (auto queue: new_events) {
queue->process_events();
}
after_callback();
}
}
EventHandlingResult handle(LoopQuitEvent event) {
_running = false;
_condition.notify_all();
return EventHandlingResult::DONE;
}
private:
std::list<EventQueueBase*> _events;
std::mutex _mutex;
std::condition_variable _condition;
bool _running = true;
};
template<typename HandlerType, typename... Ts>
std::optional<std::variant<Ts...>> EventQueue<HandlerType, Ts...>::poll() {
if (_events.empty()) {
return std::nullopt;
}
auto event = std::move(_events.front());
_events.pop_front();
return event;
}
template<typename HandlerType, typename... Ts>
template<typename T>
requires std::disjunction_v<std::is_same<T, Ts>...>
void EventQueue<HandlerType, Ts...>::push(T&& event) {
_events.emplace_back(std::forward<T>(event));
_loop->notify_pending(static_cast<HandlerType*>(this));
}
#endif // EVENT_HPP

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,120 @@
//
// Created by Stepan Usatiuk on 26.07.2025.
//
#ifndef GRIDWINDOW_HPP
#define GRIDWINDOW_HPP
#include <string>
#include "Fonts.hpp"
#include "SubSurface.hpp"
#include "Window.hpp"
#include "utils.hpp"
template<typename SurfaceType, typename PixelType, unsigned nWidth, unsigned nHeight>
class GridWindow : public Window<SurfaceType, PixelType> {
public:
explicit GridWindow(SurfaceType* owner) : Window<SurfaceType, PixelType>(owner) {
for (int i = 0; i < nWidth; ++i) {
for (int j = 0; j < nHeight; ++j) {
_grid[i][j].emplace(owner);
}
}
}
EventHandlingResult handle(KeyboardEvent keyboardEvent) override {
if (keyboardEvent.key_code == Key::Escape) {
if (!_has_focus) {
return EventHandlingResult::CONTINUE;
} else {
auto res = _grid[_current_focus_x][_current_focus_y]->get_window()->handle(keyboardEvent);
if (res == EventHandlingResult::DONE) {
return EventHandlingResult::DONE;
} else {
_has_focus = false;
}
}
} else if (keyboardEvent.key_code == Key::Enter) {
if (!_has_focus) {
_has_focus = true;
} else {
return _grid[_current_focus_x][_current_focus_y]->get_window()->handle(keyboardEvent);
}
} else {
if (_has_focus) {
return _grid[_current_focus_x][_current_focus_y]->get_window()->handle(keyboardEvent);
}
if (keyboardEvent.key_code == Key::Left) {
if (_current_focus_x > 0) {
_current_focus_x--;
}
} else if (keyboardEvent.key_code == Key::Right) {
if (_current_focus_x < nWidth - 1) {
_current_focus_x++;
}
} else if (keyboardEvent.key_code == Key::Up) {
if (_current_focus_y > 0) {
_current_focus_y--;
}
} else if (keyboardEvent.key_code == Key::Down) {
if (_current_focus_y < nHeight - 1) {
_current_focus_y++;
}
}
}
refresh();
return EventHandlingResult::DONE;
}
EventHandlingResult handle(SurfaceResizeEvent resize) override {
_cell_width = this->_owner->get_width() / nWidth;
_cell_height = this->_owner->get_height() / nHeight;
for (int i = 0; i < nWidth; ++i) {
for (int j = 0; j < nHeight; ++j) {
_grid[i][j]->set_pos(i * _cell_width + 1, j * _cell_height + 1, _cell_width - 1, _cell_height - 1);
}
}
refresh();
return EventHandlingResult::DONE;
}
template<typename WindowType, typename... Args>
void set_window(unsigned x, unsigned y, Args&&... args) {
_grid[x][y]->template set_window<WindowType>(std::forward<Args>(args)...);
}
SubSurface<SurfaceType, PixelType>& get_subsurface(unsigned x, unsigned y) {
// assert(x >= nWidth && y >= nHeight);
return *_grid[x][y];
}
void refresh() override {
for (int i = 0; i < nWidth; ++i) {
for (int j = 0; j < nHeight; ++j) {
if (_grid[i][j]->has_window()) {
_grid[i][j]->get_window()->refresh();
}
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));
} else {
this->_owner->draw_rect(i * _cell_width, j * _cell_height, _cell_width, _cell_height,
PixelType(false));
}
}
}
}
private:
std::array<std::array<std::optional<SubSurface<SurfaceType, PixelType>>, nWidth>, nHeight> _grid;
unsigned _cell_width = 0;
unsigned _cell_height = 0;
unsigned _current_focus_x = 0;
unsigned _current_focus_y = 0;
bool _has_focus = false;
};
#endif // TEXTWINDOW_HPP

View File

@@ -0,0 +1,21 @@
//
// Created by Stepan Usatiuk on 26.07.2025.
//
#ifndef PIXEL_HPP
#define PIXEL_HPP
class Pixel {
};
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); }
};
#endif //PIXEL_HPP

View File

@@ -0,0 +1,163 @@
//
// Created by Stepan Usatiuk on 26.07.2025.
//
#ifndef STANDARDEVENTS_HPP
#define STANDARDEVENTS_HPP
#include "Event.hpp"
// TODO: rewrite this
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
enum class Key {
Unknown = -1, //!< Unhandled key
A = 0, //!< The A key
B, //!< The B key
C, //!< The C key
D, //!< The D key
E, //!< The E key
F, //!< The F key
G, //!< The G key
H, //!< The H key
I, //!< The I key
J, //!< The J key
K, //!< The K key
L, //!< The L key
M, //!< The M key
N, //!< The N key
O, //!< The O key
P, //!< The P key
Q, //!< The Q key
R, //!< The R key
S, //!< The S key
T, //!< The T key
U, //!< The U key
V, //!< The V key
W, //!< The W key
X, //!< The X key
Y, //!< The Y key
Z, //!< The Z key
Num0, //!< The 0 key
Num1, //!< The 1 key
Num2, //!< The 2 key
Num3, //!< The 3 key
Num4, //!< The 4 key
Num5, //!< The 5 key
Num6, //!< The 6 key
Num7, //!< The 7 key
Num8, //!< The 8 key
Num9, //!< The 9 key
Escape, //!< The Escape key
LControl, //!< The left Control key
LShift, //!< The left Shift key
LAlt, //!< The left Alt key
LSystem, //!< The left OS specific key: window (Windows and Linux), apple (macOS), ...
RControl, //!< The right Control key
RShift, //!< The right Shift key
RAlt, //!< The right Alt key
RSystem, //!< The right OS specific key: window (Windows and Linux), apple (macOS), ...
Menu, //!< The Menu key
LBracket, //!< The [ key
RBracket, //!< The ] key
Semicolon, //!< The ; key
Comma, //!< The , key
Period, //!< The . key
Apostrophe, //!< The ' key
Slash, //!< The / key
Backslash, //!< The \ key
Grave, //!< The ` key
Equal, //!< The = key
Hyphen, //!< The - key (hyphen)
Space, //!< The Space key
Enter, //!< The Enter/Return keys
Backspace, //!< The Backspace key
Tab, //!< The Tabulation key
PageUp, //!< The Page up key
PageDown, //!< The Page down key
End, //!< The End key
Home, //!< The Home key
Insert, //!< The Insert key
Delete, //!< The Delete key
Add, //!< The + key
Subtract, //!< The - key (minus, usually from numpad)
Multiply, //!< The * key
Divide, //!< The / key
Left, //!< Left arrow
Right, //!< Right arrow
Up, //!< Up arrow
Down, //!< Down arrow
Numpad0, //!< The numpad 0 key
Numpad1, //!< The numpad 1 key
Numpad2, //!< The numpad 2 key
Numpad3, //!< The numpad 3 key
Numpad4, //!< The numpad 4 key
Numpad5, //!< The numpad 5 key
Numpad6, //!< The numpad 6 key
Numpad7, //!< The numpad 7 key
Numpad8, //!< The numpad 8 key
Numpad9, //!< The numpad 9 key
F1, //!< The F1 key
F2, //!< The F2 key
F3, //!< The F3 key
F4, //!< The F4 key
F5, //!< The F5 key
F6, //!< The F6 key
F7, //!< The F7 key
F8, //!< The F8 key
F9, //!< The F9 key
F10, //!< The F10 key
F11, //!< The F11 key
F12, //!< The F12 key
F13, //!< The F13 key
F14, //!< The F14 key
F15, //!< The F15 key
Pause, //!< The Pause key
};
struct KeyboardEvent : public Event {
KeyboardEvent(Key key_code) : key_code(key_code) {}
Key key_code;
};
struct SurfaceEvent : public Event {
enum class EventType { CLOSED, OPENED };
EventType type;
};
struct SurfaceResizeEvent : public Event {
SurfaceResizeEvent(unsigned int width, unsigned int height) : width(width), height(height) {}
unsigned width;
unsigned height;
};
template<typename Derived>
using StandardEventHandler = EventHandler<Derived, KeyboardEvent, SurfaceEvent, SurfaceResizeEvent>;
template<typename Derived>
using StandardEventQueue = EventQueue<Derived, KeyboardEvent, SurfaceEvent, SurfaceResizeEvent>;
#endif // STANDARDEVENTS_HPP

View File

@@ -0,0 +1,60 @@
//
// Created by Stepan Usatiuk on 27.07.2025.
//
#ifndef SUBSURFACE_HPP
#define SUBSURFACE_HPP
#include <memory>
#include <type_traits>
#include "Pixel.hpp"
#include "StandardEvents.hpp"
#include "Surface.hpp"
#include "Window.hpp"
#include "utils.hpp"
template<typename SurfaceParent, typename PixelType>
requires std::is_base_of_v<Pixel, PixelType>
class SubSurface : public Surface<SubSurface<SurfaceParent, PixelType>, PixelType> {
public:
SubSurface(SurfaceParent* parent) : _parent(parent) {}
void draw_pixel_impl(unsigned x, unsigned y, const PixelType& pixel) {
if (x >= _x_size || y >= _y_size) {
return; // Out of bounds
}
_parent->draw_pixel(x + _x_offset, y + _y_offset, pixel);
}
void clear_impl() {
for (unsigned y = 0; y < _y_size; y++) {
for (unsigned x = 0; x < _x_size; x++) {
_parent->draw_pixel(x + _x_offset, y + _y_offset, PixelType());
}
}
}
unsigned get_width_impl() const { return _x_size; }
unsigned get_height_impl() const { return _y_size; }
void set_pos(unsigned x_offset, unsigned y_offset, unsigned x_size, unsigned y_size) {
_x_offset = x_offset;
_y_offset = y_offset;
_x_size = x_size;
_y_size = y_size;
this->handle(SurfaceResizeEvent(x_size, y_size));
}
private:
unsigned _x_offset = 0;
unsigned _y_offset = 0;
unsigned _x_size = 0;
unsigned _y_size = 0;
SurfaceParent* _parent;
};
#endif // SUBSURFACE_HPP

View File

@@ -0,0 +1,75 @@
//
// Created by Stepan Usatiuk on 26.07.2025.
//
#ifndef SURFACE_HPP
#define SURFACE_HPP
#include <memory>
#include <type_traits>
#include "Pixel.hpp"
#include "StandardEvents.hpp"
#include "Window.hpp"
#include "utils.hpp"
template<typename Derived, typename PixelType>
requires std::is_base_of_v<Pixel, PixelType>
class Surface : public StandardEventHandler<Derived> {
public:
Surface() = default;
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) {
for (unsigned i = 0; i < width; ++i) {
draw_pixel(x + i, y, pixel);
draw_pixel(x + i, y + height - 1, pixel);
}
for (unsigned i = 0; i < height; ++i) {
draw_pixel(x, y + i, pixel);
draw_pixel(x + width - 1, y + i, pixel);
}
}
void clear() { static_cast<Derived*>(this)->clear_impl(); }
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)...);
_window->refresh();
}
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, PixelType>* get_window() {
assert(has_window());
return _window.get();
}
protected:
std::unique_ptr<Window<Derived, PixelType>> _window = nullptr;
};
#endif // SURFACE_HPP

View File

@@ -0,0 +1,70 @@
//
// Created by Stepan Usatiuk on 26.07.2025.
//
#ifndef TEXTWINDOW_HPP
#define TEXTWINDOW_HPP
#include <string>
#include "Fonts.hpp"
#include "Window.hpp"
#include "utils.hpp"
template<typename StringType>
struct TextUpdateEvent : public Event {
TextUpdateEvent(StringType text) : new_text(std::move(text)) {}
StringType new_text;
};
template<typename SurfaceType, typename PixelType, typename StringType>
class TextWindow : public Window<SurfaceType, PixelType>,
public EventHandler<TextUpdateEvent<StringType>>,
public EventQueue<TextWindow<SurfaceType, PixelType, StringType>, TextUpdateEvent<StringType>> {
public:
explicit TextWindow(SurfaceType* owner, EventLoop* loop, StringType text = "") :
Window<SurfaceType, PixelType>(owner), EventQueue<TextWindow, TextUpdateEvent<StringType>>(loop, this),
_text(std::move(text)) {}
EventHandlingResult handle(SurfaceResizeEvent resize) override {
refresh();
return EventHandlingResult::DONE;
}
EventHandlingResult handle(TextUpdateEvent<StringType> event) {
_text = std::move(event.new_text);
refresh();
return EventHandlingResult::DONE;
}
void refresh() override {
this->_owner->clear();
size_t _max_col = this->_owner->get_width() / 8;
size_t _max_row = this->_owner->get_height() / 16;
int col = 0, row = 0;
for (char c: _text) {
if (c == '\n' || col >= _max_col) {
row++;
col = 0;
if (c == '\n')
continue;
}
if (row >= _max_row) {
break;
}
for (int x = 0; x < 8; x++) {
for (int y = 0; y < 16; y++) {
bool color = fonts_Terminess_Powerline[c][y] & (1 << (8 - x));
this->_owner->draw_pixel(col * 8 + x, row * 16 + y, PixelType(color));
}
}
col++;
}
}
private:
StringType _text;
};
#endif // TEXTWINDOW_HPP

View File

@@ -0,0 +1,39 @@
//
// Created by Stepan Usatiuk on 26.07.2025.
//
#ifndef WINDOW_HPP
#define WINDOW_HPP
#include <type_traits>
#include "Event.hpp"
#include "Pixel.hpp"
#include "StandardEvents.hpp"
#include "utils.hpp"
template<typename Derived, typename PixelType>
requires std::is_base_of_v<Pixel, PixelType>
class Surface;
template<typename SurfaceType, typename PixelType>
requires std::is_base_of_v<Pixel, PixelType>
class Window : StandardEventHandler<Window<SurfaceType, PixelType>> {
public:
explicit Window(SurfaceType* owner) : _owner(owner) {
// static_assert(is_specialization_of<Surface, SurfaceType>::value);
}
virtual void refresh() = 0;
virtual ~Window() = default;
virtual EventHandlingResult handle(KeyboardEvent) { return EventHandlingResult::CONTINUE; }
virtual EventHandlingResult handle(SurfaceEvent) { return EventHandlingResult::CONTINUE; }
virtual EventHandlingResult handle(SurfaceResizeEvent) { return EventHandlingResult::CONTINUE; }
protected:
SurfaceType* _owner = nullptr;
};
#endif // SURFACE_HPP

View File

@@ -0,0 +1,14 @@
//
// Created by Stepan Usatiuk on 27.07.2025.
//
#ifndef UTILS_HPP
#define UTILS_HPP
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 {};
#endif //UTILS_HPP

View File

@@ -0,0 +1,5 @@
//
// Created by Stepan Usatiuk on 26.07.2025.
//
#include "Event.hpp"

View File

@@ -0,0 +1,8 @@
//
// Created by Stepan Usatiuk on 26.07.2025.
//
#include "TextWindow.hpp"
#include "Fonts.hpp"
#include "Surface.hpp"

View File

@@ -0,0 +1,5 @@
//
// Created by Stepan Usatiuk on 26.07.2025.
//
#include "Window.hpp"

View File

@@ -0,0 +1,23 @@
include(FetchContent)
FetchContent_Declare(
googletest
URL https://github.com/google/googletest/archive/03597a01ee50ed33e9dfd640b249b4be3799d395.zip
)
# For Windows: Prevent overriding the parent project's compiler/linker settings
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
FetchContent_MakeAvailable(googletest)
enable_testing()
include(GoogleTest)
add_executable(
EventTests
src/EventTests.cpp
)
target_link_libraries(
EventTests PRIVATE
GTest::gtest_main cbsdk
)
gtest_discover_tests(EventTests DISCOVERY_TIMEOUT 600)

View File

@@ -0,0 +1,44 @@
//
// Created by Stepan Usatiuk on 26.07.2025.
//
#include <gtest/gtest.h>
#include "Event.hpp"
struct EventOne : public Event {
std::string name;
};
struct EventTwo : public Event {
int value;
};
template<typename Derived>
using TestEventHandler = EventHandler<Derived, EventOne, EventTwo>;
class EventHandlerTest : public TestEventHandler<EventHandlerTest> {
public:
template<typename T>
void handle(const T& event) {
seen_unknown = true;
}
void handle(const EventOne& event) {
seen_event_one = true;
}
bool seen_event_one = false;
bool seen_unknown = false;
};
TEST(Event, Basic) {
EventHandlerTest handler;
EventOne event_one;
EventTwo event_two;
handler.handle(event_one);
ASSERT_TRUE(handler.seen_event_one);
handler.handle(event_two);
ASSERT_TRUE(handler.seen_unknown);
}

View File

@@ -0,0 +1,43 @@
cmake_minimum_required(VERSION 3.10)
include(FetchContent)
FetchContent_Declare(SFML
GIT_REPOSITORY https://github.com/SFML/SFML.git
GIT_TAG 3.0.1
GIT_SHALLOW ON
EXCLUDE_FROM_ALL
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)

View File

@@ -0,0 +1,41 @@
//
// Created by Stepan Usatiuk on 26.07.2025.
//
#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> {
public:
SfmlSurface(EventLoop* loop);
~SfmlSurface(); // override;
void draw_pixel_impl(unsigned x, unsigned y, const BwPixel& pixel);
void clear_impl();
unsigned get_width_impl() const;
unsigned get_height_impl() const;
template<typename T>
EventHandlingResult handle(const T& event) {
return _window->handle(event);
}
EventHandlingResult handle(SurfaceResizeEvent event);
sf::RenderWindow _sf_window;
sf::Image _image;
sf::Texture _texture;
sf::Sprite _sprite;
};
#endif // SFMLWINDOW_HPP

View File

@@ -0,0 +1,39 @@
//
// Created by Stepan Usatiuk on 26.07.2025.
//
#include "SfmlWindow.hpp"
void SfmlSurface::draw_pixel_impl(unsigned x, unsigned y, const BwPixel& pixel) {
_image.setPixel({x, y}, pixel.on ? sf::Color::Black : sf::Color::White);
}
void SfmlSurface::clear_impl() { _image = sf::Image(_image.getSize(), sf::Color::White); }
unsigned SfmlSurface::get_width_impl() const { return _image.getSize().x; }
unsigned SfmlSurface::get_height_impl() const { return _image.getSize().y; }
EventHandlingResult SfmlSurface::handle(SurfaceResizeEvent event) {
_sf_window.clear();
_image.resize({event.width, event.height});
_texture.resize({event.width, event.height});
_texture.update(_image);
_sprite = sf::Sprite(_texture);
sf::FloatRect view({0, 0}, {static_cast<float>(event.width), static_cast<float>(event.height)});
_sf_window.setView(sf::View(view));
return _window->handle(event);
}
SfmlSurface::SfmlSurface(EventLoop* loop) :
Surface<SfmlSurface, BwPixel>(),
EventQueue<SfmlSurface, 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);
_sf_window.clear();
_sf_window.draw(_sprite);
_sf_window.display();
}
SfmlSurface::~SfmlSurface() {}

View File

@@ -0,0 +1,66 @@
#include <SFML/Graphics.hpp>
#include <optional>
#include <thread>
#include "GridWindow.hpp"
#include "SfmlWindow.hpp"
#include "TextWindow.hpp"
int main() {
EventLoop loop;
SfmlSurface surface(&loop);
surface.set_window<GridWindow<SfmlSurface, BwPixel, 2, 2>>();
GridWindow<SfmlSurface, BwPixel, 2, 2>* window =
static_cast<GridWindow<SfmlSurface, BwPixel, 2, 2>*>(surface.get_window());
window->set_window<TextWindow<SubSurface<SfmlSurface, BwPixel>, BwPixel, std::string>>(0, 0, &loop, "hello");
window->set_window<TextWindow<SubSurface<SfmlSurface, BwPixel>, BwPixel, std::string>>(0, 1, &loop, "hello1");
window->set_window<GridWindow<SubSurface<SfmlSurface, BwPixel>, BwPixel, 2, 2>>(1, 0);
GridWindow<SubSurface<SfmlSurface, BwPixel>, BwPixel, 2, 2>* window2 =
static_cast<GridWindow<SubSurface<SfmlSurface, BwPixel>, BwPixel, 2, 2>*>(
window->get_subsurface(1, 0).get_window());
window->set_window<TextWindow<SubSurface<SfmlSurface, BwPixel>, BwPixel, std::string>>(1, 1, &loop, "hello3");
window2->set_window<TextWindow<SubSurface<SubSurface<SfmlSurface, BwPixel>, BwPixel>, BwPixel, std::string>>(
0, 0, &loop, "hello2");
window2->set_window<TextWindow<SubSurface<SubSurface<SfmlSurface, BwPixel>, BwPixel>, BwPixel, std::string>>(
0, 1, &loop, "hello4");
window2->set_window<TextWindow<SubSurface<SubSurface<SfmlSurface, BwPixel>, BwPixel>, BwPixel, std::string>>(
1, 0, &loop, "hello5");
window2->set_window<TextWindow<SubSurface<SubSurface<SfmlSurface, BwPixel>, BwPixel>, BwPixel, std::string>>(
1, 1, &loop, "hello6");
int i = 0;
std::thread loop_thread{[&] {
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, BwPixel>, BwPixel, std::string>*>(
window->get_subsurface(0, 0).get_window())
->push(TextUpdateEvent<std::string>{std::string("Hello, SFML!") + std::to_string(i++)});
});
}};
while (surface._sf_window.isOpen()) {
while (const std::optional event = surface._sf_window.pollEvent()) {
if (event->is<sf::Event::Closed>()) {
surface._sf_window.close();
loop.push(LoopQuitEvent{});
}
if (event->is<sf::Event::Resized>()) {
auto newSize = event->getIf<sf::Event::Resized>()->size;
surface.push(SurfaceResizeEvent{newSize.x, newSize.y});
}
if (event->is<sf::Event::KeyPressed>()) {
auto key = event->getIf<sf::Event::KeyPressed>();
surface.push(KeyboardEvent{static_cast<Key>(key->code)});
}
}
}
loop_thread.join();
}

View File

@@ -557,9 +557,9 @@ CONFIG_PARTITION_TABLE_MD5=y
#
# Compiler options
#
CONFIG_COMPILER_OPTIMIZATION_DEBUG=y
# CONFIG_COMPILER_OPTIMIZATION_DEBUG is not set
# CONFIG_COMPILER_OPTIMIZATION_SIZE is not set
# CONFIG_COMPILER_OPTIMIZATION_PERF is not set
CONFIG_COMPILER_OPTIMIZATION_PERF=y
# CONFIG_COMPILER_OPTIMIZATION_NONE is not set
CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_ENABLE=y
# CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT is not set
@@ -1258,7 +1258,7 @@ CONFIG_PM_RTOS_IDLE_OPT=y
CONFIG_PM_SLP_DISABLE_GPIO=y
CONFIG_PM_LIGHTSLEEP_RTC_OSC_CAL_INTERVAL=1
CONFIG_PM_POWER_DOWN_CPU_IN_LIGHT_SLEEP=y
# CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP is not set
CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP=y
# CONFIG_PM_LIGHT_SLEEP_CALLBACKS is not set
# end of Power Management
@@ -1480,7 +1480,6 @@ CONFIG_FREERTOS_IDLE_TIME_BEFORE_SLEEP=3
#
# Port
#
CONFIG_FREERTOS_TASK_FUNCTION_WRAPPER=y
# CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK is not set
CONFIG_FREERTOS_TLSP_DELETION_CALLBACKS=y
# CONFIG_FREERTOS_TASK_PRE_DELETION_HOOK is not set
@@ -2154,166 +2153,3 @@ CONFIG_WIFI_PROV_BLE_SEC_CONN=y
# end of Component config
# CONFIG_IDF_EXPERIMENTAL_FEATURES is not set
# Deprecated options for backward compatibility
# CONFIG_APP_BUILD_TYPE_ELF_RAM is not set
# CONFIG_NO_BLOBS is not set
# CONFIG_LOG_BOOTLOADER_LEVEL_NONE is not set
# CONFIG_LOG_BOOTLOADER_LEVEL_ERROR is not set
# CONFIG_LOG_BOOTLOADER_LEVEL_WARN is not set
CONFIG_LOG_BOOTLOADER_LEVEL_INFO=y
# CONFIG_LOG_BOOTLOADER_LEVEL_DEBUG is not set
# CONFIG_LOG_BOOTLOADER_LEVEL_VERBOSE is not set
CONFIG_LOG_BOOTLOADER_LEVEL=3
# CONFIG_APP_ROLLBACK_ENABLE is not set
# CONFIG_FLASH_ENCRYPTION_ENABLED is not set
# CONFIG_FLASHMODE_QIO is not set
# CONFIG_FLASHMODE_QOUT is not set
CONFIG_FLASHMODE_DIO=y
# CONFIG_FLASHMODE_DOUT is not set
CONFIG_MONITOR_BAUD=115200
CONFIG_OPTIMIZATION_LEVEL_DEBUG=y
CONFIG_COMPILER_OPTIMIZATION_LEVEL_DEBUG=y
CONFIG_COMPILER_OPTIMIZATION_DEFAULT=y
# CONFIG_OPTIMIZATION_LEVEL_RELEASE is not set
# CONFIG_COMPILER_OPTIMIZATION_LEVEL_RELEASE is not set
CONFIG_OPTIMIZATION_ASSERTIONS_ENABLED=y
# CONFIG_OPTIMIZATION_ASSERTIONS_SILENT is not set
# CONFIG_OPTIMIZATION_ASSERTIONS_DISABLED is not set
CONFIG_OPTIMIZATION_ASSERTION_LEVEL=2
# CONFIG_CXX_EXCEPTIONS is not set
# CONFIG_STACK_CHECK_NONE is not set
# CONFIG_STACK_CHECK_NORM is not set
CONFIG_STACK_CHECK_STRONG=y
# CONFIG_STACK_CHECK_ALL is not set
CONFIG_STACK_CHECK=y
CONFIG_WARN_WRITE_STRINGS=y
# CONFIG_ESP32_APPTRACE_DEST_TRAX is not set
CONFIG_ESP32_APPTRACE_DEST_NONE=y
CONFIG_ESP32_APPTRACE_LOCK_ENABLE=y
# CONFIG_BLUEDROID_ENABLED is not set
CONFIG_NIMBLE_ENABLED=y
CONFIG_NIMBLE_MEM_ALLOC_MODE_INTERNAL=y
# CONFIG_NIMBLE_MEM_ALLOC_MODE_DEFAULT is not set
CONFIG_NIMBLE_MAX_CONNECTIONS=3
CONFIG_NIMBLE_MAX_BONDS=3
CONFIG_NIMBLE_MAX_CCCDS=8
CONFIG_NIMBLE_L2CAP_COC_MAX_NUM=0
CONFIG_NIMBLE_PINNED_TO_CORE=0
CONFIG_NIMBLE_TASK_STACK_SIZE=4096
CONFIG_BT_NIMBLE_TASK_STACK_SIZE=4096
CONFIG_NIMBLE_ROLE_CENTRAL=y
CONFIG_NIMBLE_ROLE_PERIPHERAL=y
CONFIG_NIMBLE_ROLE_BROADCASTER=y
CONFIG_NIMBLE_ROLE_OBSERVER=y
# CONFIG_NIMBLE_NVS_PERSIST is not set
CONFIG_NIMBLE_SM_LEGACY=y
CONFIG_NIMBLE_SM_SC=y
# CONFIG_NIMBLE_SM_SC_DEBUG_KEYS is not set
CONFIG_BT_NIMBLE_SM_SC_LVL=0
# CONFIG_NIMBLE_DEBUG is not set
CONFIG_NIMBLE_SVC_GAP_DEVICE_NAME="nimble"
CONFIG_NIMBLE_GAP_DEVICE_NAME_MAX_LEN=31
CONFIG_NIMBLE_ATT_PREFERRED_MTU=256
CONFIG_NIMBLE_SVC_GAP_APPEARANCE=0
CONFIG_BT_NIMBLE_MSYS1_BLOCK_COUNT=24
CONFIG_BT_NIMBLE_ACL_BUF_COUNT=24
CONFIG_BT_NIMBLE_ACL_BUF_SIZE=255
CONFIG_BT_NIMBLE_HCI_EVT_BUF_SIZE=70
CONFIG_BT_NIMBLE_HCI_EVT_HI_BUF_COUNT=30
CONFIG_BT_NIMBLE_HCI_EVT_LO_BUF_COUNT=8
# CONFIG_NIMBLE_HS_FLOW_CTRL is not set
CONFIG_NIMBLE_RPA_TIMEOUT=900
# CONFIG_NIMBLE_MESH is not set
CONFIG_NIMBLE_CRYPTO_STACK_MBEDTLS=y
# CONFIG_BT_NIMBLE_COEX_PHY_CODED_TX_RX_TLIM_EN is not set
CONFIG_BT_NIMBLE_COEX_PHY_CODED_TX_RX_TLIM_DIS=y
CONFIG_SW_COEXIST_ENABLE=y
CONFIG_ESP32_WIFI_SW_COEXIST_ENABLE=y
CONFIG_ESP_WIFI_SW_COEXIST_ENABLE=y
# CONFIG_MCPWM_ISR_IN_IRAM is not set
# CONFIG_EVENT_LOOP_PROFILING is not set
CONFIG_POST_EVENTS_FROM_ISR=y
CONFIG_POST_EVENTS_FROM_IRAM_ISR=y
CONFIG_GDBSTUB_SUPPORT_TASKS=y
CONFIG_GDBSTUB_MAX_TASKS=32
# CONFIG_OTA_ALLOW_HTTP is not set
CONFIG_ESP_SYSTEM_PD_FLASH=y
CONFIG_ESP32_PHY_CALIBRATION_AND_DATA_STORAGE=y
CONFIG_ESP32_PHY_MAX_WIFI_TX_POWER=20
CONFIG_ESP32_PHY_MAX_TX_POWER=20
# CONFIG_REDUCE_PHY_TX_POWER is not set
# CONFIG_ESP32_REDUCE_PHY_TX_POWER is not set
CONFIG_ESP_SYSTEM_PM_POWER_DOWN_CPU=y
CONFIG_ESP32_RTC_XTAL_BOOTSTRAP_CYCLES=0
CONFIG_SYSTEM_EVENT_QUEUE_SIZE=32
CONFIG_SYSTEM_EVENT_TASK_STACK_SIZE=2304
CONFIG_MAIN_TASK_STACK_SIZE=3584
CONFIG_CONSOLE_UART_DEFAULT=y
# CONFIG_CONSOLE_UART_CUSTOM is not set
# CONFIG_CONSOLE_UART_NONE is not set
# CONFIG_ESP_CONSOLE_UART_NONE is not set
CONFIG_CONSOLE_UART=y
CONFIG_CONSOLE_UART_NUM=0
CONFIG_CONSOLE_UART_BAUDRATE=115200
CONFIG_INT_WDT=y
CONFIG_INT_WDT_TIMEOUT_MS=300
CONFIG_TASK_WDT=y
CONFIG_ESP_TASK_WDT=y
# CONFIG_TASK_WDT_PANIC is not set
CONFIG_TASK_WDT_TIMEOUT_S=5
CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU0=y
# CONFIG_ESP32_DEBUG_STUBS_ENABLE is not set
CONFIG_BROWNOUT_DET=y
# CONFIG_BROWNOUT_DET_LVL_SEL_7 is not set
# CONFIG_BROWNOUT_DET_LVL_SEL_6 is not set
# CONFIG_BROWNOUT_DET_LVL_SEL_5 is not set
# CONFIG_BROWNOUT_DET_LVL_SEL_4 is not set
CONFIG_BROWNOUT_DET_LVL_SEL_3=y
# CONFIG_BROWNOUT_DET_LVL_SEL_2 is not set
# CONFIG_BROWNOUT_DET_LVL_SEL_1 is not set
# CONFIG_BROWNOUT_DET_LVL_SEL_0 is not set
CONFIG_BROWNOUT_DET_LVL=3
CONFIG_IPC_TASK_STACK_SIZE=1024
CONFIG_TIMER_TASK_STACK_SIZE=3584
# CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH is not set
# CONFIG_ESP32_ENABLE_COREDUMP_TO_UART is not set
CONFIG_ESP32_ENABLE_COREDUMP_TO_NONE=y
CONFIG_TIMER_TASK_PRIORITY=1
CONFIG_TIMER_TASK_STACK_DEPTH=2048
CONFIG_TIMER_QUEUE_LENGTH=10
# CONFIG_ENABLE_STATIC_TASK_CLEAN_UP_HOOK is not set
# CONFIG_HAL_ASSERTION_SILIENT is not set
# CONFIG_L2_TO_L3_COPY is not set
CONFIG_ESP_GRATUITOUS_ARP=y
CONFIG_GARP_TMR_INTERVAL=60
CONFIG_TCPIP_RECVMBOX_SIZE=32
CONFIG_TCP_MAXRTX=12
CONFIG_TCP_SYNMAXRTX=12
CONFIG_TCP_MSS=1440
CONFIG_TCP_MSL=60000
CONFIG_TCP_SND_BUF_DEFAULT=5760
CONFIG_TCP_WND_DEFAULT=5760
CONFIG_TCP_RECVMBOX_SIZE=6
CONFIG_TCP_QUEUE_OOSEQ=y
CONFIG_TCP_OVERSIZE_MSS=y
# CONFIG_TCP_OVERSIZE_QUARTER_MSS is not set
# CONFIG_TCP_OVERSIZE_DISABLE is not set
CONFIG_UDP_RECVMBOX_SIZE=6
CONFIG_TCPIP_TASK_STACK_SIZE=3072
CONFIG_TCPIP_TASK_AFFINITY_NO_AFFINITY=y
# CONFIG_TCPIP_TASK_AFFINITY_CPU0 is not set
CONFIG_TCPIP_TASK_AFFINITY=0x7FFFFFFF
# CONFIG_PPP_SUPPORT is not set
CONFIG_ESP32_PTHREAD_TASK_PRIO_DEFAULT=5
CONFIG_ESP32_PTHREAD_TASK_STACK_SIZE_DEFAULT=3072
CONFIG_ESP32_PTHREAD_STACK_MIN=768
CONFIG_ESP32_PTHREAD_TASK_CORE_DEFAULT=-1
CONFIG_ESP32_PTHREAD_TASK_NAME_DEFAULT="pthread"
CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_ABORTS=y
# CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_FAILS is not set
# CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_ALLOWED is not set
CONFIG_SUPPRESS_SELECT_DEBUG_OUTPUT=y
CONFIG_SUPPORT_TERMIOS=y
CONFIG_SEMIHOSTFS_MAX_MOUNT_POINTS=1
# End of deprecated options