Files
p-rad/src/hal/hal_linux.cpp
T
2026-08-08 11:24:27 +02:00

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