311 lines
10 KiB
C++
311 lines
10 KiB
C++
#include "../core/const.hpp"
|
|
#include <SDL3/SDL.h>
|
|
#include <SDL3/SDL_events.h>
|
|
#include <SDL3/SDL_hints.h>
|
|
#include <SDL3/SDL_init.h>
|
|
#include <SDL3/SDL_iostream.h>
|
|
#include <SDL3/SDL_mouse.h>
|
|
#include <SDL3/SDL_rect.h>
|
|
#include <SDL3/SDL_render.h>
|
|
#include <SDL3/SDL_scancode.h>
|
|
#include <SDL3/SDL_stdinc.h>
|
|
#include <SDL3/SDL_surface.h>
|
|
#include <SDL3/SDL_timer.h>
|
|
#include <SDL3/SDL_video.h>
|
|
#include <SDL3_image/SDL_image.h>
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
|
|
#include "../core/color.h"
|
|
#include "core/config.hpp"
|
|
#include "draw.hpp"
|
|
#include "hal/hal.hpp"
|
|
#include <curl/curl.h>
|
|
#include <curl/easy.h>
|
|
#include <iostream>
|
|
#include <nlohmann/json.hpp>
|
|
#include <nlohmann/json_fwd.hpp>
|
|
#include <numbers>
|
|
#include <tuple>
|
|
|
|
struct sdl_session {
|
|
SDL_Window *window;
|
|
SDL_Renderer *renderer;
|
|
};
|
|
|
|
sdl_session main_sdl_session;
|
|
const SDL_DisplayMode *mode;
|
|
CURL *curl;
|
|
|
|
namespace hal {
|
|
int init() {
|
|
#ifdef CONFIG_SDL_TARGET_KMSDRM
|
|
if (!SDL_SetHint(SDL_HINT_KMSDRM_DEVICE_INDEX, "-1")) {
|
|
SDL_Log("hint failed: %s", SDL_GetError());
|
|
}
|
|
|
|
if (!SDL_SetHint(SDL_HINT_KMSDRM_REQUIRE_DRM_MASTER, "1")) {
|
|
SDL_Log("hint failed: %s", SDL_GetError());
|
|
}
|
|
if (!SDL_SetHint(SDL_HINT_VIDEO_DRIVER, "kmsdrm")) {
|
|
SDL_Log("hint failed: %s", SDL_GetError());
|
|
}
|
|
if (!SDL_SetHint(SDL_HINT_RENDER_DRIVER, CONFIG_SDL_DRIVERS)) {
|
|
SDL_Log("hint failed: %s", SDL_GetError());
|
|
}
|
|
#endif
|
|
if (!SDL_Init(SDL_INIT_VIDEO)) {
|
|
SDL_Log("init failed: %s", SDL_GetError());
|
|
return 1;
|
|
}
|
|
// DEBUG
|
|
std::clog << "active driver: " << SDL_GetCurrentVideoDriver() << "\n";
|
|
|
|
mode = SDL_GetCurrentDisplayMode(SDL_GetPrimaryDisplay());
|
|
if (!mode) {
|
|
SDL_Log("failed to get display mode: %s", SDL_GetError());
|
|
return 1;
|
|
}
|
|
main_sdl_session.window =
|
|
SDL_CreateWindow(program_name, mode->w, mode->h, SDL_WINDOW_FULLSCREEN);
|
|
if (!main_sdl_session.window) {
|
|
SDL_Log("window failed: %s", SDL_GetError());
|
|
return 1;
|
|
}
|
|
main_sdl_session.renderer = SDL_CreateRenderer(main_sdl_session.window,
|
|
#ifndef CONFIG_SDL_TARGET_KMSDRM
|
|
CONFIG_SDL_DRIVERS
|
|
#else
|
|
NULL
|
|
#endif
|
|
);
|
|
if (!main_sdl_session.renderer) {
|
|
SDL_Log("renderer failed: %s", SDL_GetError());
|
|
return 1;
|
|
}
|
|
|
|
curl = curl_easy_init();
|
|
return 0;
|
|
}
|
|
|
|
std::tuple<uint16_t, uint16_t> get_screen_size() { return {mode->w, mode->h}; }
|
|
uint64_t frame_start;
|
|
void start_frame() {
|
|
frame_start = SDL_GetTicksNS();
|
|
SDL_SetRenderDrawColor(main_sdl_session.renderer, 0, 0, 0, 255);
|
|
SDL_RenderClear(main_sdl_session.renderer);
|
|
}
|
|
|
|
constexpr Uint64 frame_delay_ns = 1000000000ULL / 1;
|
|
void end_frame() {
|
|
SDL_Event event;
|
|
while (SDL_PollEvent(&event)) {
|
|
switch (event.type) {
|
|
// Handles Window close (the 'X' button), Alt+F4, or SIGINT (Ctrl+C)
|
|
|
|
// Handles keyboard key presses
|
|
case SDL_EVENT_KEY_DOWN:
|
|
// Check if the pressed key is 'Q'
|
|
if (event.key.key != SDLK_Q) {
|
|
break;
|
|
}
|
|
[[fallthrough]];
|
|
case SDL_EVENT_QUIT:
|
|
SDL_Quit();
|
|
std::exit(0);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
SDL_RenderPresent(main_sdl_session.renderer);
|
|
|
|
uint64_t frame_time = SDL_GetTicksNS() - frame_start;
|
|
|
|
if (frame_time < frame_delay_ns) {
|
|
SDL_DelayNS(frame_delay_ns - frame_time);
|
|
}
|
|
}
|
|
} // namespace hal
|
|
|
|
namespace draw {
|
|
void pixel(const uint16_t x, const uint16_t y, const color_t color) {
|
|
SDL_SetRenderDrawColor(main_sdl_session.renderer, color.r, color.g, color.b,
|
|
0xff);
|
|
SDL_RenderPoint(main_sdl_session.renderer, x, y);
|
|
}
|
|
|
|
void filled_rect(const rect_t *rect, const color_t color) {
|
|
SDL_SetRenderDrawColor(main_sdl_session.renderer, color.r, color.g, color.b,
|
|
0xff);
|
|
const SDL_FRect frect = {
|
|
static_cast<float>(rect->x), static_cast<float>(rect->y),
|
|
static_cast<float>(rect->w), static_cast<float>(rect->h)};
|
|
SDL_RenderFillRect(main_sdl_session.renderer, &frect);
|
|
}
|
|
|
|
void circle(const uint16_t x_center, const uint16_t y_center,
|
|
const uint16_t radius, const color_t color) {
|
|
SDL_SetRenderDrawColor(main_sdl_session.renderer, color.r, color.g, color.b,
|
|
0xff);
|
|
|
|
// based on
|
|
// https://www.geeksforgeeks.org/dsa/mid-point-circle-drawing-algorithm/
|
|
int x = radius, y = 0;
|
|
|
|
// Printing the initial point on the axes
|
|
// after translation
|
|
SDL_RenderPoint(main_sdl_session.renderer, x + x_center, y + y_center);
|
|
|
|
// When radius is zero only a single
|
|
// point will be printed
|
|
if (radius > 0) {
|
|
SDL_RenderPoint(main_sdl_session.renderer, x + x_center, -y + y_center);
|
|
SDL_RenderPoint(main_sdl_session.renderer, y + x_center, x + y_center);
|
|
SDL_RenderPoint(main_sdl_session.renderer, -y + x_center, x + y_center);
|
|
}
|
|
|
|
// Initialising the value of P
|
|
int P = 1 - radius;
|
|
while (x > y) {
|
|
y++;
|
|
|
|
// Mid-point is inside or on the perimeter
|
|
if (P <= 0)
|
|
P = P + 2 * y + 1;
|
|
// Mid-point is outside the perimeter
|
|
else {
|
|
x--;
|
|
P = P + 2 * y - 2 * x + 1;
|
|
}
|
|
|
|
// All the perimeter points have already been printed
|
|
if (x < y)
|
|
break;
|
|
|
|
// Printing the generated point and its reflection
|
|
// in the other octants after translation
|
|
SDL_RenderPoint(main_sdl_session.renderer, x + x_center, y + y_center);
|
|
SDL_RenderPoint(main_sdl_session.renderer, -x + x_center, y + y_center);
|
|
SDL_RenderPoint(main_sdl_session.renderer, x + x_center, -y + y_center);
|
|
SDL_RenderPoint(main_sdl_session.renderer, -x + x_center, -y + y_center);
|
|
|
|
// If the generated point is on the line x = y then
|
|
// the perimeter points have already been printed
|
|
if (x != y) {
|
|
SDL_RenderPoint(main_sdl_session.renderer, y + x_center, x + y_center);
|
|
SDL_RenderPoint(main_sdl_session.renderer, -y + x_center, x + y_center);
|
|
SDL_RenderPoint(main_sdl_session.renderer, y + x_center, -x + y_center);
|
|
SDL_RenderPoint(main_sdl_session.renderer, -y + x_center, -x + y_center);
|
|
}
|
|
}
|
|
}
|
|
|
|
void rhombus(const uint16_t x_center, const uint16_t y_center,
|
|
const uint16_t radius, const color_t color) {
|
|
SDL_SetRenderDrawColor(main_sdl_session.renderer, color.r, color.g, color.b,
|
|
0xff);
|
|
const SDL_FPoint points[] = {
|
|
{static_cast<float>(x_center + radius), static_cast<float>(y_center)},
|
|
{static_cast<float>(x_center), static_cast<float>(y_center + radius)},
|
|
{static_cast<float>(x_center - radius), static_cast<float>(y_center)},
|
|
{static_cast<float>(x_center), static_cast<float>(y_center - radius)},
|
|
{static_cast<float>(x_center + radius), static_cast<float>(y_center)}};
|
|
SDL_RenderLines(main_sdl_session.renderer, points, ARRAY_LENGHT(points));
|
|
}
|
|
|
|
void line(const uint16_t x1, const uint16_t y1, const uint16_t x2,
|
|
const uint16_t y2, const color_t color) {
|
|
SDL_SetRenderDrawColor(main_sdl_session.renderer, color.r, color.g, color.b,
|
|
0xff);
|
|
SDL_RenderLine(main_sdl_session.renderer, x1, y1, x2, y2);
|
|
}
|
|
|
|
#ifdef CONFIG_VECTOR_FOLLOW_TRACK_RATE
|
|
void arc(uint16_t start_x, uint16_t start_y, float start_angle_rad, float speed /*in pps*/,float angular_speed_rads, uint16_t duration, color_t color) {
|
|
SDL_SetRenderDrawColor(main_sdl_session.renderer, color.r, color.g, color.b,
|
|
0xff);
|
|
|
|
if (speed == 0) {
|
|
SDL_RenderPoint(main_sdl_session.renderer, start_x, start_y);
|
|
return;
|
|
}
|
|
|
|
// Handle straight line case
|
|
if (std::fabs(angular_speed_rads) < 1e-6f) {
|
|
uint16_t steps = static_cast<int>(speed * duration);
|
|
for (uint16_t i = 0; i <= steps; ++i) {
|
|
float t = i / speed;
|
|
float x = start_x + std::cos(start_angle_rad) * speed * t;
|
|
float y = start_y + std::sin(start_angle_rad) * speed * t;
|
|
SDL_RenderPoint(main_sdl_session.renderer,static_cast<int>(std::round(x)), static_cast<int>(std::round(y)));
|
|
}
|
|
return;
|
|
}
|
|
|
|
float r = speed / angular_speed_rads;
|
|
|
|
// Center of circle: perpendicular to heading
|
|
float center_x = start_x - r * std::sin(start_angle_rad);
|
|
float center_y = start_y + r * std::cos(start_angle_rad);
|
|
|
|
// Angle from center to start point
|
|
float radius_angle = std::atan2(start_y - center_y, start_x - center_x);
|
|
|
|
// Step size in time — approx 1 pixel arc-length per step
|
|
float dt = 1.0f / std::max(std::fabs(speed), 1.0f);
|
|
uint16_t steps = static_cast<int>(duration / dt);
|
|
|
|
for (uint16_t i = 0; i <= steps; ++i) {
|
|
float t = i * dt;
|
|
float a = radius_angle + angular_speed_rads * t;
|
|
float x = center_x + std::fabs(r) * std::cos(a);
|
|
float y = center_y + std::fabs(r) * std::sin(a);
|
|
SDL_RenderPoint(main_sdl_session.renderer,static_cast<int>(std::round(x)), static_cast<int>(std::round(y)));
|
|
}
|
|
}
|
|
#endif
|
|
} // namespace draw
|
|
|
|
namespace net {
|
|
// Callback function to write data into a std::string
|
|
size_t WriteCallback_to_string(void *contents, size_t size, size_t nmemb,
|
|
void *userp) {
|
|
size_t totalSize = size * nmemb;
|
|
static_cast<std::string *>(userp)->append((char *)contents, totalSize);
|
|
return totalSize;
|
|
}
|
|
|
|
nlohmann::json http_get_json(const std::string &url) {
|
|
std::string response;
|
|
if (curl) {
|
|
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
|
|
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &WriteCallback_to_string);
|
|
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
|
|
|
|
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
|
|
curl_easy_setopt(curl, CURLOPT_ACCEPT_ENCODING, "gzip, deflate");
|
|
|
|
curl_easy_setopt(curl, CURLOPT_USERAGENT, user_agent);
|
|
|
|
curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L);
|
|
|
|
CURLcode curl_return_code = curl_easy_perform(curl);
|
|
if (curl_return_code != CURLE_OK) {
|
|
std::cerr << RED "[ERROR] " << RESET << "curl_easy_perform() failed: "
|
|
<< curl_easy_strerror(curl_return_code) << "\n";
|
|
exit(21);
|
|
}
|
|
int http_code = 0;
|
|
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
|
|
|
|
if (http_code < 200 || http_code >= 300) {
|
|
return nullptr;
|
|
}
|
|
return nlohmann::json::parse(response);
|
|
}
|
|
return nullptr;
|
|
}
|
|
} // namespace net
|