// // Created by william on 17/05/24. // #include #include "window.h" #include "log.h" #include "component.h" // A sequential window ID static int next_window_id = 1; /** * Easy declaration of commonly used component loop. The current component is in the 'component' variable. * Put the loop's body between FOR_EACH_COMPONENT and END_FOR_EACH_COMPONENT. */ #define FOR_EACH_COMPONENT(window) \ Component *component; \ LinkedListCursor cursor = window_create_component_cursor(window, &component); \ while (component != NULL) { #define END_FOR_EACH_COMPONENT \ component = get_next_component(&cursor); \ } static inline LinkedListCursor window_create_component_cursor(Window *window, Component **first_component) { LinkedListCursor cursor = linked_list_cursor_create(&window->components); if (cursor.current != NULL) { *first_component = cursor.current->data; } else { *first_component = NULL; } return cursor; } static inline Component *get_next_component(LinkedListCursor *cursor) { if (!linked_list_cursor_has_next(cursor)) { return NULL; } LinkedListNode *next_node = linked_list_cursor_next(cursor); return next_node->data; } Window window_create(char *title, int width, int height, int scale) { WindowSdlContext context; int renderer_flags = SDL_RENDERER_ACCELERATED; int window_flags = 0; if (SDL_Init(SDL_INIT_VIDEO) < 0) { log_error("Couldn't initialize SDL: %s", SDL_GetError()); exit(-1); } int actual_width = width * scale; int actual_height = height * scale; context.window = SDL_CreateWindow(title, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, actual_width, actual_height, window_flags); if (!context.window) { log_error("Failed to open %d x %d SDL window: %s", actual_width, actual_height, SDL_GetError()); exit(-1); } context.renderer = SDL_CreateRenderer(context.window, -1, renderer_flags); if (!context.renderer) { log_error("Failed to create renderer: %s", SDL_GetError()); SDL_DestroyWindow(context.window); exit(-1); } Window window; window.id = next_window_id++; window.width = width; window.height = height; window.scale = scale; window.sdl_context = context; window.components = linked_list_create(false); return window; } SDL_Texture *window_create_texture(Window *window, int access) { SDL_Renderer *renderer = window->sdl_context.renderer; return SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB8888, access, window->width, window->height); } void window_add_component(Window *window, Component *component) { LinkedList *components = &window->components; linked_list_add(components, component); } void window_render_components(Window *window) { FOR_EACH_COMPONENT(window) component->render(component->ref); END_FOR_EACH_COMPONENT } void window_render_texture(Window *window, SDL_Texture *texture) { SDL_Renderer *renderer = window->sdl_context.renderer; SDL_RenderClear(renderer); SDL_RenderCopy(renderer, texture, NULL, NULL); } void window_present(Window *window) { SDL_RenderPresent(window->sdl_context.renderer); } void window_destroy(Window *window) { FOR_EACH_COMPONENT(window) component->destroy(component->ref); free(component); END_FOR_EACH_COMPONENT SDL_DestroyRenderer(window->sdl_context.renderer); SDL_DestroyWindow(window->sdl_context.window); } void window_mouse_motion(Window *window, int x, int y) { FOR_EACH_COMPONENT(window) bool matching = component->mouse_motion(component->ref, x, y); if (matching) { // If a component has matched, we don't pass the event to the next ones // This will prevent, for example, clicking on a UI element behind another one. break; } END_FOR_EACH_COMPONENT } void window_mouse_click(Window *window) { FOR_EACH_COMPONENT(window) bool matching = component->mouse_click(component->ref); if (matching) { break; } END_FOR_EACH_COMPONENT }