This commit is contained in:
Bartkk
2025-03-26 20:16:50 +01:00
commit 5f47ce6cf6
3 changed files with 397 additions and 0 deletions

1
.clang-format Normal file
View File

@@ -0,0 +1 @@
BreakBeforeBraces: Attach

3
.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
cache/
clay/
a.out

393
main.c Normal file
View File

@@ -0,0 +1,393 @@
#include <SDL3/SDL_assert.h>
#include <SDL3/SDL_iostream.h>
#include <SDL3_ttf/SDL_ttf.h>
#define SDL_MAIN_USE_CALLBACKS
#include <SDL3/SDL.h>
#include <SDL3/SDL_events.h>
#include <SDL3/SDL_init.h>
#include <SDL3/SDL_main.h>
#include <SDL3/SDL_render.h>
#include <SDL3/SDL_video.h>
#include <SDL3_image/SDL_image.h>
#include <curl/curl.h>
#include <jansson.h>
#include <stdlib.h>
#define CLAY_IMPLEMENTATION
#include "clay/clay.h"
#include "clay/renderers/SDL3/clay_renderer_SDL3.c"
const Clay_Color COLOR_LIGHT = (Clay_Color){224, 215, 210, 255};
const Clay_Color COLOR_RED = (Clay_Color){168, 66, 28, 255};
const Clay_Color COLOR_ORANGE = (Clay_Color){225, 138, 50, 255};
static SDL_Window *window = NULL;
static SDL_Renderer *renderer = NULL;
static Clay_SDL3RendererData renderer_data;
static float window_w, window_h;
static float scroll_x, scroll_y;
typedef struct {
SDL_Texture *texture;
int width, height;
} image_t;
image_t images[1024];
size_t n_images;
typedef struct {
const char *url;
bool success;
size_t size;
char *data;
} request_t;
size_t write_callback(char *data, size_t size, size_t nmemb, void *userdata) {
if (userdata == NULL)
return nmemb;
size_t realsize = size * nmemb;
request_t *res = (request_t *)userdata;
char *ptr = SDL_realloc(res->data, res->size + realsize + 1);
res->data = ptr;
memcpy(&(res->data[res->size]), data, realsize);
res->size += realsize;
res->data[res->size] = 0;
return realsize;
}
unsigned long hash(unsigned char *str) {
unsigned long hash = 5381;
int c;
while (c = *str++)
hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
return hash;
}
char *request_data(const char *url, size_t *size) {
unsigned long url_hash = hash(url);
char *cache_path;
SDL_asprintf(&cache_path, "cache/%lul", url_hash);
size_t file_size;
char *cache_data = SDL_LoadFile(cache_path, &file_size);
if (cache_data != NULL) {
printf("Using cached file...\n");
return cache_data;
}
request_t request = {0};
CURL *curl = curl_easy_init();
CURLcode res;
curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_USERAGENT, "bartkk/sdl-esix");
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&request);
res = curl_easy_perform(curl);
SDL_assert(res == CURLE_OK);
curl_easy_cleanup(curl);
SDL_SaveFile(cache_path, request.data, request.size);
*size = request.size;
return request.data;
}
#define MULTI_HANDLE_COUNT 8
void request_multi_data(size_t n_requests, request_t requests[n_requests]) {
request_t *handle_request[MULTI_HANDLE_COUNT] = {0};
CURL *handles[MULTI_HANDLE_COUNT];
CURLM *multi_handle;
int n_running_handles;
int url_i = 0;
int finished_request = 0;
// Initialize curl multi handle
multi_handle = curl_multi_init();
// Initialuze curl handles
for (int i = 0; i < MULTI_HANDLE_COUNT; i++) {
handles[i] = curl_easy_init();
curl_easy_setopt(handles[i], CURLOPT_URL, "");
curl_easy_setopt(handles[i], CURLOPT_USERAGENT, "bartkk/sdl-esix");
curl_easy_setopt(handles[i], CURLOPT_WRITEFUNCTION, write_callback);
curl_multi_add_handle(multi_handle, handles[i]);
}
// While there are unfinished requests
while (finished_request < n_requests) {
CURLMsg *msg;
int msgs_left;
while ((msg = curl_multi_info_read(multi_handle, &msgs_left)) != NULL) {
if (msg->msg == CURLMSG_DONE) {
int idx;
for (idx = 0; idx < MULTI_HANDLE_COUNT; idx++) {
bool found = (msg->easy_handle == handles[idx]);
if (found)
break;
}
printf("Handle %d done!\n", idx);
if (handle_request[idx] != NULL) {
handle_request[idx]->success = true;
finished_request++;
}
curl_multi_remove_handle(multi_handle, handles[idx]);
if (url_i < n_requests) {
printf("Requesting another url...\n");
curl_easy_setopt(handles[idx], CURLOPT_WRITEDATA,
(void *)&requests[url_i]);
curl_easy_setopt(handles[idx], CURLOPT_URL, requests[url_i].url);
handle_request[idx] = &requests[url_i];
url_i++;
curl_multi_add_handle(multi_handle, handles[idx]);
}
}
}
curl_multi_perform(multi_handle, &n_running_handles);
curl_multi_poll(multi_handle, NULL, 0, 100, NULL);
}
}
void request_posts() {
CURL *curl = curl_easy_init();
request_t request = {0};
CURLcode res;
curl_easy_setopt(curl, CURLOPT_URL,
"https://e621.net/"
"posts.json?tags=solo%20raccoon%20rating%3Asafe%20order%"
"3Ascore&limit=5");
curl_easy_setopt(curl, CURLOPT_USERAGENT, "bartkk/sdl-esix");
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&request);
res = curl_easy_perform(curl);
SDL_assert(res == CURLE_OK);
if (res == CURLE_OK) {
/* printf("%s\n", response.data); */
json_t *root;
json_error_t error;
root = json_loads(request.data, 0, &error);
SDL_assert(root != NULL);
json_t *posts = json_object_get(root, "posts");
SDL_assert(posts != NULL);
json_t *post;
size_t i;
size_t n_requests = json_array_size(posts);
request_t requests[n_requests];
SDL_memset(requests, 0, sizeof(request_t) * n_requests);
json_array_foreach(posts, i, post) {
const char *file_url;
{
json_t *file = json_object_get(post, "preview");
json_t *file_url_obj = json_object_get(file, "url");
file_url = json_string_value(file_url_obj);
}
requests[i].url = file_url;
}
request_multi_data(n_requests, requests);
for (int i = 0; i < n_requests; i++) {
if (!requests[i].success)
continue;
SDL_IOStream *io = SDL_IOFromMem(requests[i].data, requests[i].size);
images[n_images].texture = IMG_LoadTexture_IO(renderer, io, true);
float width, height;
SDL_GetTextureSize(images[n_images].texture, &width, &height);
images[n_images].width = (int)width;
images[n_images].height = (int)height;
n_images++;
}
json_decref(root);
}
SDL_free(request.data);
curl_easy_cleanup(curl);
}
void clay_error_handler(Clay_ErrorData error_data) {
printf("%s", error_data.errorText.chars);
return;
}
static inline Clay_Dimensions clay_measure_text(Clay_StringSlice text,
Clay_TextElementConfig *config,
void *userdata) {
TTF_Font **fonts = userdata;
TTF_Font *font = fonts[0];
int width, height;
if (!TTF_GetStringSize(font, text.chars, text.length, &width, &height)) {
SDL_LogError(SDL_LOG_CATEGORY_ERROR, "Failed to measure text: %s\n",
SDL_GetError());
}
return (Clay_Dimensions){(float)width, (float)height};
}
SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[]) {
/* return SDL_APP_SUCCESS; */
SDL_SetAppMetadata("Example Renderer Clear", "1.0",
"com.example.renderer-clear");
if (!SDL_Init(SDL_INIT_VIDEO)) {
SDL_Log("Couldn't initialize SDL: %s", SDL_GetError());
return SDL_APP_FAILURE;
}
TTF_Init();
if (!SDL_CreateWindowAndRenderer("examples/renderer/clear", 640, 480,
SDL_WINDOW_RESIZABLE, &window, &renderer)) {
SDL_Log("Couldn't create window/renderer: %s", SDL_GetError());
return SDL_APP_FAILURE;
}
renderer_data.renderer = renderer;
renderer_data.textEngine = TTF_CreateRendererTextEngine(renderer);
renderer_data.fonts = SDL_calloc(1, sizeof(TTF_Font *));
renderer_data.fonts[0] =
TTF_OpenFont("/usr/share/fonts/TTF/Roboto-Medium.ttf", 24);
SDL_assert(renderer_data.fonts[0] != NULL);
uint64_t total_memory_size = Clay_MinMemorySize();
Clay_Arena clay_memory = (Clay_Arena){.memory = SDL_malloc(total_memory_size),
.capacity = total_memory_size};
int width, height;
SDL_GetWindowSize(window, &width, &height);
Clay_Initialize(clay_memory, (Clay_Dimensions){width, height},
(Clay_ErrorHandler){clay_error_handler});
Clay_SetMeasureTextFunction(clay_measure_text, renderer_data.fonts);
request_posts();
return SDL_APP_CONTINUE; /* carry on with the program! */
}
/* This function runs when a new event (mouse input, keypresses, etc) occurs. */
SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event) {
if (event->type == SDL_EVENT_QUIT) {
return SDL_APP_SUCCESS; /* end the program, reporting success to the OS.
*/
}
switch (event->type) {
case SDL_EVENT_WINDOW_RESIZED:
window_w = event->window.data1;
window_h = event->window.data2;
Clay_SetLayoutDimensions(
(Clay_Dimensions){event->window.data1, event->window.data2});
break;
case SDL_EVENT_MOUSE_MOTION:
Clay_SetPointerState((Clay_Vector2){event->motion.x, event->motion.y},
event->motion.state & SDL_BUTTON_LMASK);
break;
case SDL_EVENT_MOUSE_BUTTON_UP:
case SDL_EVENT_MOUSE_BUTTON_DOWN:
Clay_SetPointerState((Clay_Vector2){event->button.x, event->button.y},
event->button.button == SDL_BUTTON_LEFT);
break;
case SDL_EVENT_MOUSE_WHEEL:
scroll_x += event->wheel.x;
scroll_y += event->wheel.y;
Clay_UpdateScrollContainers(
true, (Clay_Vector2){event->wheel.x, event->wheel.y}, 0.01f);
break;
}
return SDL_APP_CONTINUE; /* carry on with the program! */
}
//
// Layout config is just a struct that can be declared statically, or inline
Clay_ElementDeclaration sidebarItemConfig = (Clay_ElementDeclaration){
.layout = {.sizing = {.width = CLAY_SIZING_GROW(0),
.height = CLAY_SIZING_FIXED(150)}},
.backgroundColor = COLOR_ORANGE};
void PostComponent(image_t *image) {
CLAY({.layout = {.sizing = {.width = CLAY_SIZING_FIXED(150),
.height = CLAY_SIZING_GROW(0)}},
.image = {.imageData = (image->texture),
.sourceDimensions = {image->width, image->height}}}) {}
}
SDL_AppResult SDL_AppIterate(void *appstate) {
SDL_RenderClear(renderer);
/* for (size_t i = 0; i < n_images; i++) { */
/* float x = (i % 6) * 100; */
/* float y = (i / 6) * 100; */
/* SDL_RenderTexture(renderer, images[i].texture, NULL, */
/* &(SDL_FRect){.x = x, .y = y, .w = 100, .h = 100}); */
/* } */
/* Clay_BeginLayout(); */
/**/
/* CLAY({.id = CLAY_ID("OuterContainer"), */
/* .layout = {.sizing = {CLAY_SIZING_GROW(0), CLAY_SIZING_GROW(0)}, */
/* .padding = CLAY_PADDING_ALL(16), */
/* .childGap = 16}, */
/* .backgroundColor = {250, 250, 255, 255}}) { */
/**/
/* CLAY({.id = CLAY_ID("MainContent"), */
/* .scroll = {.vertical = true}, */
/* .layout = {.layoutDirection = CLAY_TOP_TO_BOTTOM, */
/* .childGap = 8, */
/* .sizing = {.width = CLAY_SIZING_GROW(0), */
/* .height = CLAY_SIZING_GROW(0)}}, */
/* .backgroundColor = COLOR_LIGHT}) { */
/* for (int i = 0; i < n_images; i++) { */
/* image_t *image = &images[i]; */
/* PostComponent(image); */
/* } */
/* } */
/* } */
/* Clay_RenderCommandArray render_commands = Clay_EndLayout(); */
/* SDL_Clay_RenderClayCommands(&renderer_data, &render_commands); */
const int size = 120;
float divider = window_w / size;
float remainder = SDL_fmodf(window_w, size);
for (int i = 0; i < n_images; i++) {
image_t *image = &images[i];
int x = (i % (int)divider) * size +
remainder / divider * ((i % (int)divider) + 1);
int y = (i / (int)divider) * size + 8 * (i / (int)divider) + scroll_y * 20;
SDL_RenderTexture(renderer, image->texture,
&(SDL_FRect){.w = image->width, .h = image->height},
&(SDL_FRect){.x = x, .y = y, .w = size, .h = size});
}
SDL_RenderPresent(renderer);
SDL_Delay(5);
return SDL_APP_CONTINUE; /* carry on with the program! */
}
void SDL_AppQuit(void *appstate, SDL_AppResult result) {}