mirror of
https://github.com/usatiuk/cardboy.git
synced 2025-10-28 15:17:48 +01:00
Compare commits
21 Commits
v2-board
...
9420887392
| Author | SHA1 | Date | |
|---|---|---|---|
| 9420887392 | |||
| 7df84f1e81 | |||
| 4861d26d8a | |||
| e389a776be | |||
| 8b8d9d3a55 | |||
| 126d377836 | |||
| 3f8d90c18a | |||
| c439aecd03 | |||
| cd72c2d7df | |||
| 589c598b01 | |||
| 95a946e47f | |||
| 48d2089b69 | |||
| 3e9b7b4326 | |||
| e1004ff196 | |||
| 24df0fc825 | |||
| ab32731f4d | |||
| 474a0b2a43 | |||
| 35219c353c | |||
| 8180abed4c | |||
| 12d634ecc9 | |||
| 6a8f74384e |
1
Firmware/.gitignore
vendored
1
Firmware/.gitignore
vendored
@@ -1,3 +1,4 @@
|
||||
build
|
||||
cmake-build*
|
||||
.idea
|
||||
.cache
|
||||
|
||||
23
Firmware/.vscode/c_cpp_properties.json
vendored
Normal file
23
Firmware/.vscode/c_cpp_properties.json
vendored
Normal 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
15
Firmware/.vscode/launch.json
vendored
Normal 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
4
Firmware/.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"idf.flashType": "JTAG",
|
||||
"idf.port": "/dev/tty.usbmodem12401"
|
||||
}
|
||||
5
Firmware/components/sdk-esp/CMakeLists.txt
Normal file
5
Firmware/components/sdk-esp/CMakeLists.txt
Normal 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)
|
||||
@@ -1,5 +1,5 @@
|
||||
idf_component_register(SRCS
|
||||
src/hello_world_main.cpp
|
||||
src/app_main.cpp
|
||||
src/display.cpp
|
||||
src/bat_mon.cpp
|
||||
src/spi_global.cpp
|
||||
@@ -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 esp_timer
|
||||
INCLUDE_DIRS "include")
|
||||
|
||||
@@ -20,9 +20,10 @@ public:
|
||||
void pooler(); // FIXME:
|
||||
private:
|
||||
static inline i2c_device_config_t _dev_cfg = {
|
||||
.dev_addr_length = I2C_ADDR_BIT_LEN_7,
|
||||
.device_address = 0x70,
|
||||
.scl_speed_hz = 100000,
|
||||
.dev_addr_length = I2C_ADDR_BIT_LEN_7,
|
||||
.device_address = 0x36,
|
||||
.scl_speed_hz = 100000,
|
||||
.flags = 0,
|
||||
};
|
||||
|
||||
BatMon();
|
||||
|
||||
@@ -9,14 +9,14 @@
|
||||
#include "freertos/task.h"
|
||||
|
||||
typedef enum {
|
||||
L1 = 1 << 1,
|
||||
L2 = 1 << 6,
|
||||
L3 = 1 << 0,
|
||||
L4 = 1 << 7,
|
||||
R1 = 1 << 5,
|
||||
R2 = 1 << 2,
|
||||
R3 = 1 << 4,
|
||||
R4 = 1 << 3,
|
||||
BTN_START = 1 << 1,
|
||||
BTN_DOWN = 1 << 6,
|
||||
BTN_SELECT = 1 << 0,
|
||||
BTN_LEFT = 1 << 7,
|
||||
BTN_UP = 1 << 5,
|
||||
BTN_B = 1 << 2,
|
||||
BTN_RIGHT = 1 << 4,
|
||||
BTN_A = 1 << 3,
|
||||
} btn_num;
|
||||
|
||||
class Buttons {
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -7,10 +7,11 @@
|
||||
#define I2C_SCL GPIO_NUM_8
|
||||
#define I2C_SDA GPIO_NUM_9
|
||||
|
||||
#define SPI_MOSI GPIO_NUM_5
|
||||
#define SPI_MISO GPIO_NUM_0
|
||||
#define SPI_SCK GPIO_NUM_4
|
||||
#define SPI_DISP_CS GPIO_NUM_11
|
||||
#define SPI_MOSI GPIO_NUM_5
|
||||
#define SPI_MISO GPIO_NUM_0
|
||||
#define SPI_SCK GPIO_NUM_4
|
||||
#define SPI_DISP_CS GPIO_NUM_24
|
||||
#define SPI_DISP_DISP GPIO_NUM_11
|
||||
|
||||
#define SPI_BUS SPI2_HOST
|
||||
|
||||
@@ -20,10 +21,6 @@
|
||||
#define PWR_INT GPIO_NUM_10
|
||||
#define PWR_KILL GPIO_NUM_12
|
||||
|
||||
#define SHR_OUT GPIO_NUM_23
|
||||
#define SHR_CLK GPIO_NUM_3
|
||||
#define SHR_SH GPIO_NUM_2
|
||||
|
||||
#define DIRECT_BTN GPIO_NUM_1
|
||||
#define EXP_INT GPIO_NUM_1
|
||||
|
||||
#endif
|
||||
|
||||
@@ -4,24 +4,46 @@
|
||||
|
||||
#ifndef CB_DISP_TOOLS_HPP
|
||||
#define CB_DISP_TOOLS_HPP
|
||||
|
||||
#include <display.hpp>
|
||||
|
||||
namespace DispTools {
|
||||
static void clear() {
|
||||
for (int y = 0; y < DISP_HEIGHT; y++) {
|
||||
for (int x = 0; x < DISP_WIDTH; x++) {
|
||||
SMD::set_pixel(x, y, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
static bool get_pixel(int x, int y) {
|
||||
if (x < 0 || x >= DISP_WIDTH || y < 0 || y >= DISP_HEIGHT)
|
||||
assert(false);
|
||||
assert(false); // Not implemented
|
||||
return true;
|
||||
// return disp_frame[y][x];
|
||||
}
|
||||
static void reset_pixel(int x, int y) {
|
||||
if (x < 0 || x >= DISP_WIDTH || y < 0 || y >= DISP_HEIGHT)
|
||||
assert(false);
|
||||
SMD::set_pixel(x, y, false);
|
||||
}
|
||||
static void set_pixel(int x, int y) {
|
||||
if (x < 0 || x >= DISP_WIDTH || y < 0 || y >= DISP_HEIGHT)
|
||||
assert(false);
|
||||
|
||||
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 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);
|
||||
void draw_to_display();
|
||||
|
||||
private:
|
||||
SMD::disp_frame_t disp_frame;
|
||||
SMD::set_pixel(x, y, true);
|
||||
}
|
||||
static void set_pixel(int x, int y, bool on) {
|
||||
if (on) {
|
||||
set_pixel(x, y);
|
||||
} else {
|
||||
reset_pixel(x, y);
|
||||
}
|
||||
}
|
||||
static void draw_to_display() { SMD::draw(); }
|
||||
static void draw_to_display_async_start() { SMD::draw_async_start(); }
|
||||
static void draw_to_display_async_wait() { SMD::draw_async_wait(); }
|
||||
static bool draw_to_display_async_busy() { return SMD::draw_async_busy(); }
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -12,33 +12,74 @@
|
||||
#include <array>
|
||||
#include <bitset>
|
||||
|
||||
class SMD {
|
||||
#include "Surface.hpp"
|
||||
#include "Window.hpp"
|
||||
|
||||
namespace SMD {
|
||||
static constexpr size_t kLineBytes = DISP_WIDTH / 8;
|
||||
static constexpr size_t kLineMultiSingle = (kLineBytes + 2);
|
||||
static constexpr size_t kLineDataBytes = kLineMultiSingle * DISP_HEIGHT + 2;
|
||||
|
||||
DMA_ATTR extern uint8_t dma_buf[SMD::kLineDataBytes];
|
||||
|
||||
void init();
|
||||
void clear();
|
||||
void draw();
|
||||
// Asynchronous (DMA queued) draw API
|
||||
void draw_async_start(); // queue frame transfer if none in flight
|
||||
void draw_async_wait(); // wait for any in-flight transfer to finish
|
||||
bool draw_async_busy(); // true if a transfer is in-flight
|
||||
|
||||
static void set_pixel(int x, int y, bool value) {
|
||||
assert(x >= 0 && x < DISP_WIDTH && y >= 0 && y < DISP_HEIGHT);
|
||||
|
||||
unsigned lineIdx = 2 + kLineMultiSingle * y + (x / 8);
|
||||
unsigned bitIdx = 1 << (7 - (x % 8)) % 8;
|
||||
|
||||
if (value) {
|
||||
dma_buf[lineIdx] &= ~bitIdx;
|
||||
} else {
|
||||
dma_buf[lineIdx] |= bitIdx;
|
||||
}
|
||||
}
|
||||
|
||||
static inline spi_device_interface_config_t _devcfg = {
|
||||
.mode = 0, // SPI mode 0
|
||||
.clock_speed_hz = 2 * 1000 * 1000, // Clock out at 10 MHz
|
||||
.spics_io_num = SPI_DISP_CS, // CS pin
|
||||
.flags = SPI_DEVICE_POSITIVE_CS,
|
||||
.queue_size = 3,
|
||||
// .pre_cb = lcd_spi_pre_transfer_callback, //Specify pre-transfer callback to handle D/C line
|
||||
};
|
||||
extern spi_device_handle_t _spi;
|
||||
extern bool _vcom;
|
||||
extern bool _inFlight;
|
||||
extern spi_transaction_t _tx; // persistent transaction struct for async API
|
||||
}; // namespace SMD
|
||||
|
||||
class SMDSurface : public Surface<SMDSurface, BwPixel>, public StandardEventQueue<SMDSurface> {
|
||||
public:
|
||||
using disp_line_t = std::bitset<400>;
|
||||
using disp_frame_t = std::array<disp_line_t, 240>;
|
||||
using PixelType = BwPixel;
|
||||
|
||||
static SMD& get();
|
||||
void clear();
|
||||
void draw(const disp_frame_t& frame);
|
||||
SMDSurface(EventLoop* loop);
|
||||
|
||||
private:
|
||||
SMD();
|
||||
static inline spi_device_interface_config_t _devcfg = {
|
||||
.mode = 0, // SPI mode 0
|
||||
.clock_speed_hz = 2 * 1000 * 1000, // Clock out at 10 MHz
|
||||
.spics_io_num = SPI_DISP_CS, // CS pin
|
||||
.flags = SPI_DEVICE_POSITIVE_CS,
|
||||
.queue_size = 3,
|
||||
// .pre_cb = lcd_spi_pre_transfer_callback, //Specify pre-transfer callback to handle D/C line
|
||||
};
|
||||
static constexpr size_t kLineBytes = DISP_WIDTH / 8;
|
||||
spi_device_handle_t _spi;
|
||||
bool _vcom = false;
|
||||
~SMDSurface() override;
|
||||
|
||||
static constexpr size_t kLineData = (kLineBytes + 4);
|
||||
std::array<uint8_t, kLineData> buf{};
|
||||
void draw_pixel_impl(unsigned x, unsigned y, const BwPixel& pixel);
|
||||
|
||||
std::array<uint8_t, kLineBytes> prep_line(const SMD::disp_line_t& line);
|
||||
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
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ class Shutdowner {
|
||||
public:
|
||||
static Shutdowner& get();
|
||||
void install_isr();
|
||||
void shutdown();
|
||||
private:
|
||||
Shutdowner();
|
||||
};
|
||||
|
||||
1369
Firmware/main/src/app_main.cpp
Normal file
1369
Firmware/main/src/app_main.cpp
Normal file
File diff suppressed because it is too large
Load Diff
@@ -10,6 +10,7 @@
|
||||
#include "freertos/task.h"
|
||||
|
||||
#include "i2c_global.hpp"
|
||||
#include "shutdowner.hpp"
|
||||
|
||||
static i2c_master_dev_handle_t dev_handle;
|
||||
|
||||
@@ -20,25 +21,78 @@ BatMon& BatMon::get() {
|
||||
|
||||
static void start_pooler(void* arg) { static_cast<BatMon*>(arg)->pooler(); }
|
||||
|
||||
void WriteRegister(uint8_t reg, uint16_t value) {
|
||||
uint8_t buf2[3];
|
||||
buf2[0] = reg;
|
||||
buf2[1] = value & 0xFF;
|
||||
buf2[2] = value >> 8;
|
||||
ESP_ERROR_CHECK(i2c_master_transmit(dev_handle, buf2, sizeof(buf2), -1));
|
||||
}
|
||||
|
||||
uint16_t ReadRegister(uint8_t reg) {
|
||||
uint16_t buffer;
|
||||
ESP_ERROR_CHECK(
|
||||
i2c_master_transmit_receive(dev_handle, ®, sizeof(reg), reinterpret_cast<uint8_t*>(&buffer), 2, -1));
|
||||
return buffer;
|
||||
}
|
||||
void WriteAndVerifyRegister(char RegisterAddress, int RegisterValueToWrite) {
|
||||
int attempt = 0;
|
||||
uint16_t RegisterValueRead;
|
||||
do {
|
||||
WriteRegister(RegisterAddress, RegisterValueToWrite);
|
||||
vTaskDelay(1 / portTICK_PERIOD_MS);
|
||||
RegisterValueRead = ReadRegister(RegisterAddress);
|
||||
} while (RegisterValueToWrite != RegisterValueRead && attempt++ < 3);
|
||||
}
|
||||
|
||||
static constexpr float RSense = 0.1; // 100mOhm
|
||||
static constexpr uint16_t DesignCapMah = 180; // 100mOhm
|
||||
|
||||
constexpr float mahToCap(float mah) { return mah * (1000.0 / 5.0) * RSense; }
|
||||
constexpr float capToMah(uint16_t cap) { return cap * (5.0 / 1000.0) / RSense; }
|
||||
constexpr float regToCurrent(uint16_t reg) {
|
||||
return static_cast<float>(static_cast<int16_t>(reg)) * 0.0015625f / RSense; // Convert to mA
|
||||
}
|
||||
constexpr uint16_t currentToReg(float current) { return static_cast<uint16_t>(current * RSense / 0.0015625f); }
|
||||
constexpr float regToVoltage(uint16_t reg) {
|
||||
return reg * 0.078125f * 0.001f; // Convert to volts
|
||||
}
|
||||
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(10);
|
||||
static constexpr uint16_t VEmpty = 0b1001011001100001; // (3V/3.88V)
|
||||
static constexpr uint16_t dQAcc = (DesignCap / 32);
|
||||
|
||||
BatMon::BatMon() {
|
||||
ESP_ERROR_CHECK(i2c_master_bus_add_device(I2cGlobal::get().get_bus_handle(), &_dev_cfg, &dev_handle));
|
||||
|
||||
uint8_t reg = 1;
|
||||
uint8_t buffer;
|
||||
uint8_t buf2[2];
|
||||
|
||||
ESP_ERROR_CHECK(
|
||||
i2c_master_transmit_receive(dev_handle, ®, sizeof(reg), reinterpret_cast<uint8_t*>(&buffer), 1, -1));
|
||||
if (buffer & (1 << 4)) // POR reset
|
||||
bool StatusPOR = ReadRegister(0x00) & 0x0002;
|
||||
if (StatusPOR) // POR reset
|
||||
{
|
||||
printf("Gas gauge reset!\n");
|
||||
buf2[0] = 1;
|
||||
buf2[1] = 0 << 4;
|
||||
ESP_ERROR_CHECK(i2c_master_transmit(dev_handle, buf2, sizeof(buf2), -1));
|
||||
while (ReadRegister(0x3D) & 1)
|
||||
vTaskDelay(10 / portTICK_PERIOD_MS);
|
||||
|
||||
buf2[0] = 0;
|
||||
buf2[1] = 1 << 4 | 1 << 2; // 10 bit adc
|
||||
ESP_ERROR_CHECK(i2c_master_transmit(dev_handle, buf2, sizeof(buf2), -1));
|
||||
uint16_t HibCFG = ReadRegister(0xBA); // Store original HibCFG value
|
||||
WriteRegister(0x60, 0x90); // Exit Hibernate Mode step 1
|
||||
WriteRegister(0xBA, 0x0); // Exit Hibernate Mode step 2
|
||||
WriteRegister(0x60, 0x0); // Exit Hibernate Mode step 3
|
||||
WriteRegister(0x18, DesignCap); // Write DesignCap
|
||||
WriteRegister(0x45, DesignCap / 32); // Write dQAcc
|
||||
WriteRegister(0x1E, IchgTerm); // Write IchgTerm
|
||||
WriteRegister(0x3A, VEmpty); // Write VEmpty
|
||||
WriteRegister(0x46, dQAcc * 44138 / DesignCap); // Write dPAcc
|
||||
WriteRegister(0xDB, 0x8000); // Write ModelCFG
|
||||
|
||||
// Poll ModelCFG.Refresh(highest bit), proceed to Step 4 when ModelCFG.Refresh = 0.
|
||||
while (ReadRegister(0xDB) & 0x8000)
|
||||
vTaskDelay(10 / portTICK_PERIOD_MS); // 10ms wait loop. Do not continue until ModelCFG.Refresh == 0.
|
||||
WriteRegister(0xBA, HibCFG); // Restore Original HibCFG value
|
||||
|
||||
uint16_t Status = ReadRegister(0x00); // Read Status
|
||||
WriteAndVerifyRegister(0x00, Status & 0xFFFD); // Write and Verify Status with POR bit cleared
|
||||
}
|
||||
|
||||
xTaskCreate(&start_pooler, "BatMon", 2048, this, tskIDLE_PRIORITY, &_pooler_task);
|
||||
@@ -48,28 +102,13 @@ void BatMon::pooler() {
|
||||
while (true) {
|
||||
uint8_t reg = 8;
|
||||
uint16_t buffer;
|
||||
ESP_ERROR_CHECK(
|
||||
i2c_master_transmit_receive(dev_handle, ®, sizeof(reg), reinterpret_cast<uint8_t*>(&buffer), 2, -1));
|
||||
float voltage = buffer;
|
||||
voltage *= 2.44f;
|
||||
voltage /= 1000;
|
||||
_voltage = voltage;
|
||||
reg = 2;
|
||||
ESP_ERROR_CHECK(
|
||||
i2c_master_transmit_receive(dev_handle, ®, sizeof(reg), reinterpret_cast<uint8_t*>(&buffer), 2, -1));
|
||||
float charge = *reinterpret_cast<int16_t*>(&buffer);
|
||||
charge *= 6.70f;
|
||||
charge /= 50;
|
||||
_charge = charge;
|
||||
reg = 6;
|
||||
ESP_ERROR_CHECK(
|
||||
i2c_master_transmit_receive(dev_handle, ®, sizeof(reg), reinterpret_cast<uint8_t*>(&buffer), 2, -1));
|
||||
float current = static_cast<int16_t>(buffer << 2);
|
||||
current *= 11.77f;
|
||||
current /= 50;
|
||||
current /= 4;
|
||||
_current = current;
|
||||
_charge = capToMah(ReadRegister(0x05));
|
||||
_current = regToCurrent(ReadRegister(0x0B));
|
||||
_voltage = regToVoltage(ReadRegister(0x09));
|
||||
PowerHelper::get().delay(10000, 1000);
|
||||
if (_voltage < 3.0f) {
|
||||
Shutdowner::get().shutdown();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,14 @@
|
||||
#include "freertos/task.h"
|
||||
|
||||
#include "config.hpp"
|
||||
#include "i2c_global.hpp"
|
||||
|
||||
static i2c_master_dev_handle_t dev_handle;
|
||||
static inline i2c_device_config_t dev_cfg = {
|
||||
.dev_addr_length = I2C_ADDR_BIT_LEN_7,
|
||||
.device_address = 0x20,
|
||||
.scl_speed_hz = 100000,
|
||||
};
|
||||
|
||||
Buttons& Buttons::get() {
|
||||
static Buttons buttons;
|
||||
@@ -21,18 +29,48 @@ Buttons& Buttons::get() {
|
||||
|
||||
static void start_pooler(void* arg) { static_cast<Buttons*>(arg)->pooler(); }
|
||||
|
||||
Buttons::Buttons() {
|
||||
ESP_ERROR_CHECK(gpio_reset_pin(SHR_OUT));
|
||||
ESP_ERROR_CHECK(gpio_reset_pin(SHR_CLK));
|
||||
ESP_ERROR_CHECK(gpio_reset_pin(SHR_SH));
|
||||
ESP_ERROR_CHECK(gpio_set_direction(SHR_OUT, GPIO_MODE_INPUT));
|
||||
ESP_ERROR_CHECK(gpio_set_pull_mode(SHR_OUT, GPIO_FLOATING));
|
||||
ESP_ERROR_CHECK(gpio_set_direction(SHR_SH, GPIO_MODE_OUTPUT));
|
||||
ESP_ERROR_CHECK(gpio_set_direction(SHR_CLK, GPIO_MODE_OUTPUT));
|
||||
static bool is_on_low;
|
||||
|
||||
xTaskCreate(&start_pooler, "ButtonsPooler", 2048, this, 1, &_pooler_task);
|
||||
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];
|
||||
|
||||
// Config
|
||||
buf2[0] = 6;
|
||||
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++) {
|
||||
@@ -42,18 +80,17 @@ static void delay(unsigned long long loop) {
|
||||
|
||||
void Buttons::pooler() {
|
||||
while (true) {
|
||||
ESP_ERROR_CHECK(gpio_set_level(SHR_SH, 0));
|
||||
ESP_ERROR_CHECK(gpio_set_level(SHR_SH, 1));
|
||||
|
||||
uint8_t new_val = 0;
|
||||
|
||||
for (int i = 0; i < 8; i++) {
|
||||
ESP_ERROR_CHECK(gpio_set_level(SHR_CLK, 0));
|
||||
new_val |= gpio_get_level(SHR_OUT) << i;
|
||||
ESP_ERROR_CHECK(gpio_set_level(SHR_CLK, 1));
|
||||
}
|
||||
_current = new_val;
|
||||
PowerHelper::get().delay(10000, 100);
|
||||
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, ®, sizeof(reg), reinterpret_cast<uint8_t*>(&buffer), 1, -1));
|
||||
_current = buffer;
|
||||
// read second port too to clear the interrupt
|
||||
reg = 1;
|
||||
ESP_ERROR_CHECK(
|
||||
i2c_master_transmit_receive(dev_handle, ®, 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); }
|
||||
|
||||
@@ -8,68 +8,3 @@
|
||||
|
||||
#include <display.hpp>
|
||||
|
||||
DispTools& DispTools::get() {
|
||||
static DispTools disp_tools;
|
||||
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) {
|
||||
draw_line(x1, y1 + dy, x2, y1 + dy);
|
||||
dy += (dy > 0) ? -1 : 1;
|
||||
}
|
||||
}
|
||||
void DispTools::draw_line(int x1, int y1, int x2, int y2) {
|
||||
int dx = x2 - x1;
|
||||
int dy = y2 - y1;
|
||||
int a = 0, b = 0, diff = 0;
|
||||
|
||||
if (dx == 0) {
|
||||
while (dy != 0) {
|
||||
set_pixel(x1, y1 + dy);
|
||||
dy += (dy > 0) ? -1 : 1;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (dy == 0) {
|
||||
while (dx != 0) {
|
||||
set_pixel(x1 + dx, y1);
|
||||
dx += (dx > 0) ? -1 : 1;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
while (std::abs(a) <= std::abs(dx) && std::abs(b) <= std::abs(dy)) {
|
||||
set_pixel(x1 + a, y1 + b);
|
||||
if (diff < 0) {
|
||||
a += (dx > 0) ? 1 : -1;
|
||||
diff += std::abs(dy);
|
||||
} else {
|
||||
b += (dy > 0) ? 1 : -1;
|
||||
diff -= std::abs(dx);
|
||||
}
|
||||
}
|
||||
}
|
||||
void DispTools::draw_circle(int x, int y, int r) {
|
||||
if (r > 181)
|
||||
return;
|
||||
int dy = -r;
|
||||
while (dy <= r) {
|
||||
int dx = static_cast<int>(std::sqrt(r * r - dy * dy));
|
||||
draw_line(x - dx, y + dy, x + dx, y + dy);
|
||||
dy++;
|
||||
}
|
||||
}
|
||||
void DispTools::draw_to_display() { SMD::get().draw(disp_frame); }
|
||||
|
||||
@@ -13,9 +13,9 @@ void FbTty::draw_char(int col, int row) {
|
||||
for (int y = 0; y < 16; y++) {
|
||||
bool color = fonts_Terminess_Powerline[_buf[col][row]][y] & (1 << (8 - x));
|
||||
if (color)
|
||||
DispTools::get().set_pixel(col * 8 + x, row * 16 + y);
|
||||
DispTools::set_pixel(col * 8 + x, row * 16 + y);
|
||||
else
|
||||
DispTools::get().reset_pixel(col * 8 + x, row * 16 + y);
|
||||
DispTools::reset_pixel(col * 8 + x, row * 16 + y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,29 +6,36 @@
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include <driver/gpio.h>
|
||||
#include "driver/spi_master.h"
|
||||
|
||||
#include "disp_tools.hpp"
|
||||
|
||||
DMA_ATTR uint8_t SMD::dma_buf[SMD::kLineDataBytes]{};
|
||||
spi_device_handle_t SMD::_spi;
|
||||
bool SMD::_vcom = false;
|
||||
bool SMD::_inFlight = false;
|
||||
spi_transaction_t SMD::_tx{};
|
||||
|
||||
// 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; }
|
||||
|
||||
std::array<uint8_t, SMD::kLineBytes> SMD::prep_line(const SMD::disp_line_t& line) {
|
||||
std::array<uint8_t, kLineBytes> data{};
|
||||
for (int i = 0; i < DISP_WIDTH; i++) {
|
||||
data[i / 8] = data[i / 8] | (line[i] << (i % 8));
|
||||
}
|
||||
for (int i = 0; i < kLineBytes; i++) {
|
||||
data[i] = reverse_bits3(data[i]);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
SMD& SMD::get() {
|
||||
static SMD smd;
|
||||
return smd;
|
||||
}
|
||||
void SMD::init() {
|
||||
spi_bus_add_device(SPI_BUS, &_devcfg, &_spi);
|
||||
ESP_ERROR_CHECK(gpio_reset_pin(SPI_DISP_DISP));
|
||||
|
||||
SMD::SMD() { spi_bus_add_device(SPI_BUS, &_devcfg, &_spi); }
|
||||
ESP_ERROR_CHECK(gpio_set_direction(SPI_DISP_DISP, GPIO_MODE_OUTPUT));
|
||||
ESP_ERROR_CHECK(gpio_set_level(SPI_DISP_DISP, 1));
|
||||
ESP_ERROR_CHECK(gpio_hold_en(SPI_DISP_DISP));
|
||||
|
||||
for (uint8_t i = 0; i < DISP_HEIGHT; i++) {
|
||||
dma_buf[kLineMultiSingle * i + 1] = reverse_bits3(i + 1);
|
||||
dma_buf[2 + kLineMultiSingle * i + kLineBytes] = 0;
|
||||
}
|
||||
dma_buf[kLineDataBytes - 1] = 0;
|
||||
}
|
||||
|
||||
void SMD::clear() {
|
||||
std::array<uint8_t, 2> buf{};
|
||||
@@ -40,23 +47,61 @@ void SMD::clear() {
|
||||
ESP_ERROR_CHECK(spi_device_transmit(_spi, &t));
|
||||
}
|
||||
|
||||
void SMD::draw(const disp_frame_t& frame) {
|
||||
_vcom = !_vcom;
|
||||
for (uint8_t i = 0; i < DISP_HEIGHT; i++) {
|
||||
spi_transaction_t t{};
|
||||
|
||||
t.tx_buffer = buf.data();
|
||||
t.length = buf.size() * 8;
|
||||
|
||||
buf[0] = 0b10000000 | (_vcom << 6);
|
||||
buf[1] = reverse_bits3(i + 1);
|
||||
|
||||
auto prepared = prep_line(frame.at(i));
|
||||
memcpy(buf.data() + 2, prepared.data(), kLineBytes);
|
||||
|
||||
buf[2 + kLineBytes] = 0;
|
||||
buf[2 + kLineBytes + 1] = 0;
|
||||
|
||||
ESP_ERROR_CHECK(spi_device_transmit(_spi, &t));
|
||||
}
|
||||
void SMD::draw() {
|
||||
// Synchronous (blocking) version retained for compatibility
|
||||
_vcom = !_vcom;
|
||||
_tx = {};
|
||||
_tx.tx_buffer = dma_buf;
|
||||
_tx.length = SMD::kLineDataBytes * 8;
|
||||
dma_buf[0] = 0b10000000 | (_vcom << 6);
|
||||
ESP_ERROR_CHECK(spi_device_transmit(_spi, &_tx));
|
||||
}
|
||||
|
||||
bool SMD::draw_async_busy() { return _inFlight; }
|
||||
|
||||
void SMD::draw_async_start() {
|
||||
if (_inFlight)
|
||||
return; // already in flight
|
||||
_vcom = !_vcom;
|
||||
_tx = {};
|
||||
_tx.tx_buffer = dma_buf;
|
||||
_tx.length = SMD::kLineDataBytes * 8;
|
||||
dma_buf[0] = 0b10000000 | (_vcom << 6);
|
||||
esp_err_t err = spi_device_queue_trans(_spi, &_tx, 0);
|
||||
if (err == ESP_OK)
|
||||
_inFlight = true;
|
||||
else
|
||||
ESP_ERROR_CHECK(err);
|
||||
}
|
||||
|
||||
void SMD::draw_async_wait() {
|
||||
if (!_inFlight)
|
||||
return;
|
||||
spi_transaction_t* r = nullptr;
|
||||
esp_err_t err;
|
||||
// Wait indefinitely; could add timeout handling if desired
|
||||
err = spi_device_get_trans_result(_spi, &r, portMAX_DELAY);
|
||||
ESP_ERROR_CHECK(err);
|
||||
_inFlight = false;
|
||||
}
|
||||
|
||||
void SMDSurface::draw_pixel_impl(unsigned x, unsigned y, const BwPixel& pixel) {
|
||||
if (pixel.on)
|
||||
DispTools::set_pixel(x, y);
|
||||
else
|
||||
DispTools::reset_pixel(x, y);
|
||||
}
|
||||
|
||||
void SMDSurface::clear_impl() { DispTools::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() {}
|
||||
|
||||
@@ -1,119 +0,0 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2010-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: CC0-1.0
|
||||
*/
|
||||
|
||||
#include <buttons.hpp>
|
||||
#include <cstdint>
|
||||
#include <disp_tools.hpp>
|
||||
#include <disp_tty.hpp>
|
||||
#include <esp_pm.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
#include "esp_chip_info.h"
|
||||
#include "esp_flash.h"
|
||||
#include "esp_system.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#include "display.hpp"
|
||||
|
||||
#include "bat_mon.hpp"
|
||||
#include "driver/i2c_master.h"
|
||||
#include "driver/spi_master.h"
|
||||
#include "i2c_global.hpp"
|
||||
|
||||
#include <driver/gpio.h>
|
||||
#include <esp_sleep.h>
|
||||
#include <memory>
|
||||
#include <power_helper.hpp>
|
||||
#include <shutdowner.hpp>
|
||||
#include <spi_global.hpp>
|
||||
#include <string>
|
||||
|
||||
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));
|
||||
printf("Hello world!\n");
|
||||
// TODO: Where to put that?
|
||||
ESP_ERROR_CHECK(esp_sleep_enable_gpio_wakeup());
|
||||
// For some reason, calling it here hangs on startup, sometimes
|
||||
// ESP_ERROR_CHECK(gpio_install_isr_service(0));
|
||||
PowerHelper::get();
|
||||
Shutdowner::get();
|
||||
ESP_ERROR_CHECK(gpio_install_isr_service(0));
|
||||
Shutdowner::get().install_isr();
|
||||
PowerHelper::get().install_isr();
|
||||
Buttons::get();
|
||||
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_circle(100, 100, 20);
|
||||
DispTools::get().draw_to_display();
|
||||
tty.putstr("Hello\nworld!");
|
||||
DispTools::get().draw_to_display();
|
||||
|
||||
int rx = 30, ry = 30;
|
||||
|
||||
int lastmove = 0;
|
||||
|
||||
while (true) {
|
||||
// SMD::clear();
|
||||
// printf("Voltage: %f\n", BatMon::get_voltage());
|
||||
DispTools::get().clear();
|
||||
tty.reset();
|
||||
|
||||
uint8_t pressed = Buttons::get().get_pressed();
|
||||
if (pressed & L3)
|
||||
rx -= 5;
|
||||
if (pressed & L4)
|
||||
ry += 5;
|
||||
if (pressed & R3)
|
||||
ry -= 5;
|
||||
if (pressed & R4)
|
||||
rx += 5;
|
||||
|
||||
if (pressed == 0 && !PowerHelper::get().is_slow())
|
||||
lastmove++;
|
||||
else if (pressed != 0) {
|
||||
lastmove = 0;
|
||||
PowerHelper::get().set_slow(false);
|
||||
}
|
||||
|
||||
if (lastmove > 20) {
|
||||
lastmove = 0;
|
||||
PowerHelper::get().set_slow(true);
|
||||
}
|
||||
|
||||
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" : "");
|
||||
|
||||
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();
|
||||
}
|
||||
@@ -25,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(DIRECT_BTN));
|
||||
ESP_ERROR_CHECK(gpio_set_direction(DIRECT_BTN, GPIO_MODE_INPUT));
|
||||
ESP_ERROR_CHECK(gpio_set_pull_mode(DIRECT_BTN, GPIO_FLOATING));
|
||||
ESP_ERROR_CHECK(gpio_set_intr_type(DIRECT_BTN, GPIO_INTR_HIGH_LEVEL));
|
||||
ESP_ERROR_CHECK(gpio_wakeup_enable(DIRECT_BTN, GPIO_INTR_HIGH_LEVEL));
|
||||
// ESP_ERROR_CHECK(gpio_install_isr_service(0));
|
||||
// gpio_isr_handler_add(DIRECT_BTN, 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()) {
|
||||
@@ -67,4 +57,6 @@ void PowerHelper::delay(int slow_ms, int normal_ms) {
|
||||
vTaskDelay(normal_ms / portTICK_PERIOD_MS);
|
||||
}
|
||||
}
|
||||
void PowerHelper::install_isr() { gpio_isr_handler_add(DIRECT_BTN, wakeup, this); }
|
||||
void PowerHelper::install_isr() {
|
||||
// gpio_isr_handler_add(EXP_INT, wakeup, this);
|
||||
}
|
||||
|
||||
@@ -14,26 +14,31 @@ Shutdowner& Shutdowner::get() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
static void IRAM_ATTR shutdown(void* arg) {
|
||||
static void IRAM_ATTR int_shutdown(void* arg) {
|
||||
// printf("Shutting down...\n");
|
||||
ESP_ERROR_CHECK(gpio_hold_dis(PWR_KILL));
|
||||
ESP_ERROR_CHECK(gpio_set_level(PWR_KILL, 0));
|
||||
}
|
||||
|
||||
Shutdowner::Shutdowner() {
|
||||
ESP_ERROR_CHECK(gpio_reset_pin(PWR_INT));
|
||||
ESP_ERROR_CHECK(gpio_reset_pin(PWR_KILL));
|
||||
void Shutdowner::shutdown() {
|
||||
ESP_ERROR_CHECK(gpio_hold_dis(PWR_KILL));
|
||||
ESP_ERROR_CHECK(gpio_set_level(PWR_KILL, 0));
|
||||
}
|
||||
|
||||
ESP_ERROR_CHECK(gpio_set_direction(PWR_INT, GPIO_MODE_INPUT));
|
||||
Shutdowner::Shutdowner() {
|
||||
ESP_ERROR_CHECK(gpio_reset_pin(PWR_KILL));
|
||||
ESP_ERROR_CHECK(gpio_set_direction(PWR_KILL, GPIO_MODE_OUTPUT));
|
||||
ESP_ERROR_CHECK(gpio_set_level(PWR_KILL, 1));
|
||||
ESP_ERROR_CHECK(gpio_hold_en(PWR_KILL));
|
||||
|
||||
ESP_ERROR_CHECK(gpio_reset_pin(PWR_INT));
|
||||
ESP_ERROR_CHECK(gpio_set_direction(PWR_INT, GPIO_MODE_INPUT));
|
||||
ESP_ERROR_CHECK(gpio_set_pull_mode(PWR_INT, GPIO_FLOATING));
|
||||
ESP_ERROR_CHECK(gpio_set_intr_type(PWR_INT, GPIO_INTR_LOW_LEVEL));
|
||||
// ESP_ERROR_CHECK(esp_sleep_enable_gpio_wakeup());
|
||||
ESP_ERROR_CHECK(gpio_wakeup_enable(PWR_INT, GPIO_INTR_LOW_LEVEL));
|
||||
// ESP_ERROR_CHECK(gpio_install_isr_service(0));
|
||||
ESP_ERROR_CHECK(gpio_hold_en(PWR_KILL));
|
||||
// gpio_isr_handler_add(PWR_INT, shutdown, nullptr);
|
||||
}
|
||||
|
||||
void Shutdowner::install_isr() { gpio_isr_handler_add(PWR_INT, shutdown, nullptr); }
|
||||
void Shutdowner::install_isr() { gpio_isr_handler_add(PWR_INT, int_shutdown, nullptr); }
|
||||
|
||||
76
Firmware/sdk/.clang-format
Normal file
76
Firmware/sdk/.clang-format
Normal 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
|
||||
...
|
||||
11
Firmware/sdk/CMakeLists.txt
Normal file
11
Firmware/sdk/CMakeLists.txt
Normal 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 ()
|
||||
0
Firmware/sdk/examples/CMakeLists.txt
Normal file
0
Firmware/sdk/examples/CMakeLists.txt
Normal file
22
Firmware/sdk/library/CMakeLists.txt
Normal file
22
Firmware/sdk/library/CMakeLists.txt
Normal 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 ()
|
||||
139
Firmware/sdk/library/include_public/Event.hpp
Normal file
139
Firmware/sdk/library/include_public/Event.hpp
Normal file
@@ -0,0 +1,139 @@
|
||||
//
|
||||
// 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>
|
||||
#include <variant>
|
||||
#include <functional>
|
||||
|
||||
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
|
||||
4879
Firmware/sdk/library/include_public/Fonts.hpp
Normal file
4879
Firmware/sdk/library/include_public/Fonts.hpp
Normal file
File diff suppressed because it is too large
Load Diff
128
Firmware/sdk/library/include_public/GridWindow.hpp
Normal file
128
Firmware/sdk/library/include_public/GridWindow.hpp
Normal file
@@ -0,0 +1,128 @@
|
||||
//
|
||||
// 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, unsigned nWidth, unsigned nHeight>
|
||||
class GridWindow : public Window<SurfaceType> {
|
||||
public:
|
||||
using PixelType = typename SurfaceType::PixelType;
|
||||
|
||||
explicit GridWindow(SurfaceType* owner) : Window<SurfaceType>(owner) {
|
||||
for (int i = 0; i < nWidth; ++i) {
|
||||
for (int j = 0; j < nHeight; ++j) {
|
||||
_grid[i][j].emplace(owner);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EventHandlingResult handle_v(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_v(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) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
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>& get_subsurface(unsigned x, unsigned y) {
|
||||
// assert(x >= nWidth && y >= nHeight);
|
||||
return *_grid[x][y];
|
||||
}
|
||||
|
||||
void refresh() {
|
||||
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));
|
||||
} else {
|
||||
this->_owner->draw_rect(i * _cell_width, j * _cell_height, _cell_width, _cell_height,
|
||||
PixelType(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;
|
||||
|
||||
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
|
||||
21
Firmware/sdk/library/include_public/Pixel.hpp
Normal file
21
Firmware/sdk/library/include_public/Pixel.hpp
Normal 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
|
||||
163
Firmware/sdk/library/include_public/StandardEvents.hpp
Normal file
163
Firmware/sdk/library/include_public/StandardEvents.hpp
Normal 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
|
||||
58
Firmware/sdk/library/include_public/SubSurface.hpp
Normal file
58
Firmware/sdk/library/include_public/SubSurface.hpp
Normal file
@@ -0,0 +1,58 @@
|
||||
//
|
||||
// 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>
|
||||
class SubSurface : public Surface<SubSurface<SurfaceParent>, typename SurfaceParent::PixelType> {
|
||||
public:
|
||||
using PixelType = typename SurfaceParent::PixelType;
|
||||
|
||||
SubSurface(SurfaceParent* parent) : _parent(parent) {}
|
||||
SubSurface(SubSurface<SurfaceParent>* parent) : _parent(parent->_parent) {}
|
||||
|
||||
void draw_pixel_impl(unsigned x, unsigned y, const PixelType& pixel) {
|
||||
if (x >= _x_size || y >= _y_size) {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
_parent->draw_pixel(x + _x_offset, y + _y_offset, pixel);
|
||||
}
|
||||
|
||||
unsigned get_x_offset() const { return _x_offset; }
|
||||
|
||||
unsigned get_y_offset() const { return _y_offset; }
|
||||
|
||||
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
|
||||
81
Firmware/sdk/library/include_public/Surface.hpp
Normal file
81
Firmware/sdk/library/include_public/Surface.hpp
Normal file
@@ -0,0 +1,81 @@
|
||||
//
|
||||
// Created by Stepan Usatiuk on 26.07.2025.
|
||||
//
|
||||
|
||||
#ifndef SURFACE_HPP
|
||||
#define SURFACE_HPP
|
||||
|
||||
#include <cassert>
|
||||
#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() { static_assert(std::is_same_v<PixelType, typename Derived::PixelType>); }
|
||||
|
||||
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() {
|
||||
for (unsigned x = 0; x < get_width(); x++) {
|
||||
for (unsigned y = 0; y < get_height(); y++) {
|
||||
draw_pixel(x, y, PixelType());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
protected:
|
||||
std::unique_ptr<Window<Derived>> _window = nullptr;
|
||||
};
|
||||
|
||||
#endif // SURFACE_HPP
|
||||
72
Firmware/sdk/library/include_public/TextWindow.hpp
Normal file
72
Firmware/sdk/library/include_public/TextWindow.hpp
Normal file
@@ -0,0 +1,72 @@
|
||||
//
|
||||
// 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 StringType>
|
||||
class TextWindow : public Window<SurfaceType>,
|
||||
public EventHandler<TextUpdateEvent<StringType>>,
|
||||
public EventQueue<TextWindow<SurfaceType, StringType>, TextUpdateEvent<StringType>> {
|
||||
public:
|
||||
using PixelType = typename SurfaceType::PixelType;
|
||||
|
||||
explicit TextWindow(SurfaceType* owner, EventLoop* loop, StringType text = "") :
|
||||
Window<SurfaceType>(owner), EventQueue<TextWindow, TextUpdateEvent<StringType>>(loop, this),
|
||||
_text(std::move(text)) {}
|
||||
|
||||
EventHandlingResult handle_v(SurfaceResizeEvent resize) override {
|
||||
refresh();
|
||||
return EventHandlingResult::DONE;
|
||||
}
|
||||
|
||||
EventHandlingResult handle(TextUpdateEvent<StringType> event) {
|
||||
_text = std::move(event.new_text);
|
||||
refresh();
|
||||
return EventHandlingResult::DONE;
|
||||
}
|
||||
|
||||
void refresh() {
|
||||
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
|
||||
40
Firmware/sdk/library/include_public/Window.hpp
Normal file
40
Firmware/sdk/library/include_public/Window.hpp
Normal file
@@ -0,0 +1,40 @@
|
||||
//
|
||||
// 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>
|
||||
class Window : StandardEventHandler<Window<SurfaceType>> {
|
||||
public:
|
||||
using PixelType = typename SurfaceType::PixelType;
|
||||
|
||||
explicit Window(SurfaceType* owner) : _owner(owner) {
|
||||
// static_assert(is_specialization_of<Surface, SurfaceType>::value);
|
||||
}
|
||||
|
||||
virtual ~Window() = default;
|
||||
|
||||
EventHandlingResult handle(auto Event) { return handle_v(Event); }
|
||||
|
||||
virtual EventHandlingResult handle_v(KeyboardEvent) { return EventHandlingResult::CONTINUE; }
|
||||
virtual EventHandlingResult handle_v(SurfaceEvent) { return EventHandlingResult::CONTINUE; }
|
||||
virtual EventHandlingResult handle_v(SurfaceResizeEvent) { return EventHandlingResult::CONTINUE; }
|
||||
|
||||
protected:
|
||||
SurfaceType* _owner = nullptr;
|
||||
};
|
||||
|
||||
#endif // SURFACE_HPP
|
||||
14
Firmware/sdk/library/include_public/utils.hpp
Normal file
14
Firmware/sdk/library/include_public/utils.hpp
Normal 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
|
||||
5
Firmware/sdk/library/src/Event.cpp
Normal file
5
Firmware/sdk/library/src/Event.cpp
Normal file
@@ -0,0 +1,5 @@
|
||||
//
|
||||
// Created by Stepan Usatiuk on 26.07.2025.
|
||||
//
|
||||
|
||||
#include "Event.hpp"
|
||||
8
Firmware/sdk/library/src/TextWindow.cpp
Normal file
8
Firmware/sdk/library/src/TextWindow.cpp
Normal file
@@ -0,0 +1,8 @@
|
||||
//
|
||||
// Created by Stepan Usatiuk on 26.07.2025.
|
||||
//
|
||||
|
||||
#include "TextWindow.hpp"
|
||||
#include "Fonts.hpp"
|
||||
#include "Surface.hpp"
|
||||
|
||||
5
Firmware/sdk/library/src/Window.cpp
Normal file
5
Firmware/sdk/library/src/Window.cpp
Normal file
@@ -0,0 +1,5 @@
|
||||
//
|
||||
// Created by Stepan Usatiuk on 26.07.2025.
|
||||
//
|
||||
|
||||
#include "Window.hpp"
|
||||
23
Firmware/sdk/library/test/CMakeLists.txt
Normal file
23
Firmware/sdk/library/test/CMakeLists.txt
Normal 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)
|
||||
44
Firmware/sdk/library/test/src/EventTests.cpp
Normal file
44
Firmware/sdk/library/test/src/EventTests.cpp
Normal 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);
|
||||
}
|
||||
43
Firmware/sdk/sfml-port/CMakeLists.txt
Normal file
43
Firmware/sdk/sfml-port/CMakeLists.txt
Normal 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)
|
||||
41
Firmware/sdk/sfml-port/include_public/SfmlWindow.hpp
Normal file
41
Firmware/sdk/sfml-port/include_public/SfmlWindow.hpp
Normal 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:
|
||||
using PixelType = BwPixel;
|
||||
|
||||
SfmlSurface(EventLoop* loop);
|
||||
|
||||
~SfmlSurface(); // override;
|
||||
|
||||
void draw_pixel_impl(unsigned x, unsigned y, const BwPixel& pixel);
|
||||
|
||||
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
|
||||
37
Firmware/sdk/sfml-port/src/SfmlWindow.cpp
Normal file
37
Firmware/sdk/sfml-port/src/SfmlWindow.cpp
Normal file
@@ -0,0 +1,37 @@
|
||||
//
|
||||
// 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);
|
||||
}
|
||||
|
||||
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() {}
|
||||
75
Firmware/sdk/sfml-port/src/main.cpp
Normal file
75
Firmware/sdk/sfml-port/src/main.cpp
Normal file
@@ -0,0 +1,75 @@
|
||||
#include <SFML/Graphics.hpp>
|
||||
#include <barrier>
|
||||
#include <latch>
|
||||
|
||||
#include <optional>
|
||||
#include <thread>
|
||||
|
||||
#include "GridWindow.hpp"
|
||||
#include "SfmlWindow.hpp"
|
||||
#include "TextWindow.hpp"
|
||||
|
||||
int main() {
|
||||
EventLoop loop;
|
||||
std::latch barrier{1};
|
||||
|
||||
SfmlSurface* surface_ptr;
|
||||
int i = 0;
|
||||
std::thread loop_thread{[&] {
|
||||
SfmlSurface surface(&loop);
|
||||
surface_ptr = &surface;
|
||||
|
||||
barrier.count_down();
|
||||
|
||||
surface.set_window<GridWindow<SfmlSurface, 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>*>(
|
||||
window->get_subsurface(1, 0).get_window());
|
||||
window->set_window<TextWindow<SubSurface<SfmlSurface>, std::string>>(1, 1, &loop, "hello3");
|
||||
|
||||
window2->set_window<TextWindow<SubSurface<SfmlSurface>, std::string>>(
|
||||
0, 0, &loop, "hello2");
|
||||
window2->set_window<TextWindow<SubSurface<SfmlSurface>, std::string>>(
|
||||
0, 1, &loop, "hello4");
|
||||
window2->set_window<TextWindow<SubSurface<SfmlSurface>, std::string>>(
|
||||
1, 0, &loop, "hello5");
|
||||
window2->set_window<TextWindow<SubSurface<SfmlSurface>, 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>*>(
|
||||
window->get_subsurface(0, 0).get_window())
|
||||
->push(TextUpdateEvent<std::string>{std::string("Hello, SFML!") + std::to_string(i++)});
|
||||
});
|
||||
}};
|
||||
|
||||
barrier.wait();
|
||||
|
||||
while (surface_ptr->_sf_window.isOpen()) {
|
||||
while (const std::optional event = surface_ptr->_sf_window.pollEvent()) {
|
||||
if (event->is<sf::Event::Closed>()) {
|
||||
surface_ptr->_sf_window.close();
|
||||
loop.push(LoopQuitEvent{});
|
||||
}
|
||||
if (event->is<sf::Event::Resized>()) {
|
||||
auto newSize = event->getIf<sf::Event::Resized>()->size;
|
||||
surface_ptr->push(SurfaceResizeEvent{newSize.x, newSize.y});
|
||||
}
|
||||
if (event->is<sf::Event::KeyPressed>()) {
|
||||
auto key = event->getIf<sf::Event::KeyPressed>();
|
||||
surface_ptr->push(KeyboardEvent{static_cast<Key>(key->code)});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
loop_thread.join();
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user