#include #include #include #define SDL_MAIN_USE_CALLBACKS #include #include #include #include #include #include #include #include #include #include #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) {}