Initial
This commit is contained in:
1
.clang-format
Normal file
1
.clang-format
Normal file
@@ -0,0 +1 @@
|
|||||||
|
BreakBeforeBraces: Attach
|
||||||
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
cache/
|
||||||
|
clay/
|
||||||
|
a.out
|
||||||
393
main.c
Normal file
393
main.c
Normal 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) {}
|
||||||
Reference in New Issue
Block a user