nesemu/gui/components/window.c

134 lines
4.0 KiB
C

//
// Created by william on 17/05/24.
//
#include <SDL.h>
#include "window.h"
#include "log.h"
#include "component.h"
/**
* 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.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
}