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