From f18ad715fb2aabff4159bddb13a87ba4a3069318 Mon Sep 17 00:00:00 2001 From: william Date: Sun, 1 Sep 2024 15:54:41 -0400 Subject: [PATCH] SDL menu in top of main window --- .idea/inspectionProfiles/Project_Default.xml | 8 + CMakeLists.txt | 5 +- README.md | 2 +- debugger/program_view.h | 2 +- gui/CMakeLists.txt | 9 +- gui/components/CMakeLists.txt | 9 + gui/components/component.h | 20 ++ gui/components/linked_list.c | 87 +++++++ gui/components/linked_list.h | 71 ++++++ gui/components/window.c | 134 ++++++++++ gui/components/window.h | 91 +++++++ gui/components/window_menu.c | 251 +++++++++++++++++++ gui/components/window_menu.h | 110 ++++++++ gui/gui.c | 51 ++-- gui/gui.h | 2 + gui/main_window.c | 44 +++- gui/main_window.h | 11 +- gui/nametable_window.c | 13 +- gui/nametable_window.h | 6 +- gui/pattern_window.c | 13 +- gui/pattern_window.h | 4 +- gui/window.c | 101 -------- gui/window.h | 18 -- main.c | 5 +- utils/CMakeLists.txt | 4 - utils/linked_list.c | 126 ---------- utils/linked_list.h | 82 ------ 27 files changed, 883 insertions(+), 396 deletions(-) create mode 100644 .idea/inspectionProfiles/Project_Default.xml create mode 100644 gui/components/CMakeLists.txt create mode 100644 gui/components/component.h create mode 100644 gui/components/linked_list.c create mode 100644 gui/components/linked_list.h create mode 100644 gui/components/window.c create mode 100644 gui/components/window.h create mode 100644 gui/components/window_menu.c create mode 100644 gui/components/window_menu.h delete mode 100644 gui/window.c delete mode 100644 gui/window.h delete mode 100644 utils/CMakeLists.txt delete mode 100644 utils/linked_list.c delete mode 100644 utils/linked_list.h diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..8aa9bde --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index d055958..dc181dc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,7 +15,6 @@ add_subdirectory(cpu) add_subdirectory(ppu) add_subdirectory(mappers) add_subdirectory(rom) -add_subdirectory(utils) add_subdirectory(gui) list(APPEND EXTRA_INCLUDES @@ -23,8 +22,6 @@ list(APPEND EXTRA_INCLUDES "${PROJECT_SOURCE_DIR}/ppu" "${PROJECT_SOURCE_DIR}/mappers" "${PROJECT_SOURCE_DIR}/rom" - "${PROJECT_SOURCE_DIR}/debugger" - "${PROJECT_SOURCE_DIR}/utils" "${PROJECT_SOURCE_DIR}/gui") set(HEADERS include/system.h include/types.h) @@ -32,7 +29,7 @@ set(SOURCE main.c system.c) add_executable(nes_emulator ${HEADERS} ${SOURCE}) -target_link_libraries(nes_emulator nes_cpu nes_ppu nes_mappers nes_rom nes_utils nes_gui log.c) +target_link_libraries(nes_emulator nes_cpu nes_ppu nes_mappers nes_rom nes_gui log.c) target_include_directories(nes_emulator PUBLIC "${PROJECT_BINARY_DIR}" ${EXTRA_INCLUDES}) \ No newline at end of file diff --git a/README.md b/README.md index 3df2e58..d7d5229 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ only tested on Linux. Here is how to run the project: - PPU - Registers: Done - VRAM: Done - - Background rendering: In Progress + - Background rendering: Done - Sprite rendering: To Do - Input: To Do - APU: To Do diff --git a/debugger/program_view.h b/debugger/program_view.h index 4646556..e1e59bc 100644 --- a/debugger/program_view.h +++ b/debugger/program_view.h @@ -10,7 +10,7 @@ #include "../cpu/decoding.h" #include "cursor.h" #include "window.h" -#include "../utils/linked_list.h" +#include "components/linked_list.h" #define PROGRAM_VIEW_HEIGHT 19 #define PROGRAM_VIEW_WIDTH 42 diff --git a/gui/CMakeLists.txt b/gui/CMakeLists.txt index 06bb855..d93b2e4 100644 --- a/gui/CMakeLists.txt +++ b/gui/CMakeLists.txt @@ -1,5 +1,5 @@ -set(HEADERS gui.h window.h main_window.h) -set(SOURCE gui.c window.c main_window.c) +set(HEADERS gui.h main_window.h) +set(SOURCE gui.c main_window.c) if (NES_DEBUG) list(APPEND HEADERS char_map.h pattern_window.h nametable_window.h dbg_pattern_table.h dbg_nametable.h dbg_palette.h) @@ -8,7 +8,6 @@ endif (NES_DEBUG) add_library(nes_gui ${SOURCE} ${HEADERS}) -find_package(SDL2 REQUIRED) -include_directories(nes_gui ${SDL2_INCLUDE_DIRS}) +add_subdirectory(components) -target_link_libraries(nes_gui log.c ${SDL2_LIBRARIES} SDL2_ttf) \ No newline at end of file +target_link_libraries(nes_gui nes_gui_components log.c SDL2_ttf) \ No newline at end of file diff --git a/gui/components/CMakeLists.txt b/gui/components/CMakeLists.txt new file mode 100644 index 0000000..5427b72 --- /dev/null +++ b/gui/components/CMakeLists.txt @@ -0,0 +1,9 @@ +set(HEADERS component.h linked_list.h window.h window_menu.h) +set(SOURCE linked_list.c window.c window_menu.c) + +add_library(nes_gui_components ${SOURCE} ${HEADERS}) + +find_package(SDL2 REQUIRED) +include_directories(nes_gui_components ${SDL2_INCLUDE_DIRS}) + +target_link_libraries(nes_gui_components ${SDL2_LIBRARIES}) \ No newline at end of file diff --git a/gui/components/component.h b/gui/components/component.h new file mode 100644 index 0000000..debe5c0 --- /dev/null +++ b/gui/components/component.h @@ -0,0 +1,20 @@ +// +// Created by william on 8/24/24. +// + +#ifndef NES_EMULATOR_COMPONENT_H +#define NES_EMULATOR_COMPONENT_H + +typedef struct component { + void *ref; + + void (*render)(void *ref); + + void (*destroy)(void *ref); + + bool (*mouse_motion)(void *ref, int x, int y); + + bool (*mouse_click)(void *ref); +} Component; + +#endif //NES_EMULATOR_COMPONENT_H diff --git a/gui/components/linked_list.c b/gui/components/linked_list.c new file mode 100644 index 0000000..7f2e9fb --- /dev/null +++ b/gui/components/linked_list.c @@ -0,0 +1,87 @@ +// +// Created by william on 1/16/24. +// + +#include +#include +#include +#include +#include "linked_list.h" + +LinkedList linked_list_create(bool circular) { + LinkedList list; + + list.circular = circular; + list.size = 0; + list.head = NULL; + list.end = NULL; + + return list; +} + +void linked_list_add(LinkedList *list, void *data) { + assert(list != NULL); + assert(data != NULL); + + LinkedListNode *node = malloc(sizeof(LinkedListNode)); + if (node == NULL) { + perror("Failed to allocate memory for linked list node"); + exit(EXIT_FAILURE); + } + + node->data = data; + node->previous = list->end; + + if (list->head == NULL) { + list->head = node; + } + + if (list->end != NULL) { + list->end->next = node; + } + + if (list->circular) { + node->next = list->head; + } else { + node->next = NULL; + } + + list->end = node; + list->size++; +} + +void linked_list_destroy(LinkedList *list) { + assert(list != NULL); + + LinkedListNode *node = list->head; + while (node != NULL) { + LinkedListNode *current_node = node; + node = node->next; + + free(current_node); + + if (node == list->head) { + // The list may be circular, we don't want an infinite free loop + break; + } + } +} + +LinkedListCursor linked_list_cursor_create(LinkedList *list) { + LinkedListCursor cursor = {list->head}; + return cursor; +} + +LinkedListNode *linked_list_cursor_next(LinkedListCursor *cursor) { + if (cursor->current == NULL) { + return NULL; + } + + LinkedListNode *next_node = cursor->current->next; + cursor->current = next_node; + return next_node; +} + +bool linked_list_cursor_has_next(LinkedListCursor *cursor) { + return cursor->current != NULL && cursor->current->next != NULL; +} \ No newline at end of file diff --git a/gui/components/linked_list.h b/gui/components/linked_list.h new file mode 100644 index 0000000..cf29b10 --- /dev/null +++ b/gui/components/linked_list.h @@ -0,0 +1,71 @@ +// +// Created by william on 1/16/24. +// + +#include + +#ifndef NESEMULATOR_LINKED_LIST_H +#define NESEMULATOR_LINKED_LIST_H + +typedef struct linked_list_node { + struct linked_list_node *previous; + struct linked_list_node *next; + void *data; +} LinkedListNode; + +typedef struct linked_list { + bool circular; + unsigned int size; + LinkedListNode *head; + LinkedListNode *end; +} LinkedList; + +typedef struct linked_list_cursor { + LinkedListNode *current; +} LinkedListCursor; + +/** + * Initializes a new linked list. + * + * @param circular If the list is circular, meaning that the last node is linked to the first node. + * @return The linked list instance + */ +LinkedList linked_list_create(bool circular); + +/** + * Adds data to a linked list. + * + * @param list The linked list + * @param data The data to add + */ +void linked_list_add(LinkedList *list, void *data); + +/** + * Destroys a linked list. The data must be freed before. + * + * @param list The list to destroy + */ +void linked_list_destroy(LinkedList *list); + +/** + * Creates a read cursor for a linked list. + * @param list The list + * @return A cursor initialized to the first item in the list. + */ +LinkedListCursor linked_list_cursor_create(LinkedList *list); + +/** + * Gets the next node in the list. + * + * @param cursor A read cursor for the list + * @return The next node in the list. Can be NULL if the list is empty or depleted. + */ +LinkedListNode *linked_list_cursor_next(LinkedListCursor *cursor); + +/** + * Checks if there is nodes remaining after a cursor. + * @param cursor A cursor + */ +bool linked_list_cursor_has_next(LinkedListCursor *cursor); + +#endif //NESEMULATOR_LINKED_LIST_H diff --git a/gui/components/window.c b/gui/components/window.c new file mode 100644 index 0000000..a2d86ad --- /dev/null +++ b/gui/components/window.c @@ -0,0 +1,134 @@ +// +// Created by william on 17/05/24. +// + +#include +#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 +} \ No newline at end of file diff --git a/gui/components/window.h b/gui/components/window.h new file mode 100644 index 0000000..78be6aa --- /dev/null +++ b/gui/components/window.h @@ -0,0 +1,91 @@ +// +// Created by william on 17/05/24. +// + +#ifndef NES_EMULATOR_WINDOW_H +#define NES_EMULATOR_WINDOW_H + +#include +#include "linked_list.h" +#include "component.h" + +typedef struct window_sdl_context { + SDL_Renderer *renderer; + SDL_Window *window; +} WindowSdlContext; + +typedef struct window { + int width; + int height; + int scale; + + WindowSdlContext sdl_context; + LinkedList components; +} Window; + +/** + * Creates a window. + * @param title The title of the window + * @param width The width in pixels + * @param height The height in pixels + * @param scale The scale of pixels of this window (number of real pixels per drawn pixel) + * @return The created window + */ +Window window_create(char *title, int width, int height, int scale); + +/** + * Creates a background_texture for a window. (same width/height) + * @param window A reference to the window + * @param access The SDL background_texture access type + * @return An SDL background_texture for the window + */ +SDL_Texture *window_create_texture(Window *window, int access); + +/** + * Adds a component to a window. + * @param window A reference to the window + * @param component A reference to the component to add + */ +void window_add_component(Window *window, Component *component); + +/** + * Renders the components of a window (but not the window itself) + * @param window The window to render components + */ +void window_render_components(Window *window); + +/** + * Renders a background_texture in the window. + * @param window A reference to the window + * @param texture The background_texture to render + */ +void window_render_texture(Window *window, SDL_Texture *texture); + +/** + * Presents a window. + * @param window A reference to the window + */ +void window_present(Window *window); + +/** + * Destroys a window. + * Free the ressources of the window and its components. + * @param window The window to destroy + */ +void window_destroy(Window *window); + +/** + * Handles mouse motion events in the window. + * @param window A reference to the window + * @param x The x position of the mouse + * @param y The y position of the mouse + */ +void window_mouse_motion(Window *window, int x, int y); + +/** + * Handles mouse click events in the window. + * @param window A reference to the window + */ +void window_mouse_click(Window *window); + +#endif //NES_EMULATOR_WINDOW_H \ No newline at end of file diff --git a/gui/components/window_menu.c b/gui/components/window_menu.c new file mode 100644 index 0000000..cbe2b95 --- /dev/null +++ b/gui/components/window_menu.c @@ -0,0 +1,251 @@ +// +// Created by william on 8/23/24. +// + +#include +#include +#include "window_menu.h" +#include "../../include/types.h" +#include "log.h" + +/** + * Easy declaration of commonly used item loop. The current item is in the 'item' variable. + * Put the loop's body between FOR_EACH_ITEM and END_FOR_EACH_ITEM. + */ +#define FOR_EACH_ITEM_(list)\ + MenuItemComponent *item; \ + LinkedListCursor cursor = menu_create_item_cursor(&(list), &item); \ + while (item != NULL) { + +#define FOR_EACH_ITEM(menu) FOR_EACH_ITEM_((menu)->items) +#define FOR_EACH_SUBITEM(menu_item) FOR_EACH_ITEM_((menu_item)->sub_items) + +#define END_FOR_EACH_ITEM \ + item = menu_get_next_item(&cursor); \ + } + +#define CREATE_PIXEL_TEXTURE(texture, menu, color) \ + pixel texture ## _buffer[1] = {color}; \ + (menu)->texture = SDL_CreateTexture((menu)->renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STATIC, 1, 1); \ + SDL_UpdateTexture((menu)->texture, NULL, &texture ## _buffer, sizeof(pixel)) + +static inline LinkedListCursor menu_create_item_cursor(LinkedList *list, MenuItemComponent **first_component) { + LinkedListCursor cursor = linked_list_cursor_create(list); + if (cursor.current != NULL) { + *first_component = cursor.current->data; + } else { + *first_component = NULL; + } + + return cursor; +} + +static inline MenuItemComponent *menu_get_next_item(LinkedListCursor *cursor) { + if (!linked_list_cursor_has_next(cursor)) { + return NULL; + } + + LinkedListNode *next_node = linked_list_cursor_next(cursor); + return next_node->data; +} + +MenuComponent *menu_create(Window *window, TTF_Font *font) { + MenuComponent *menu = malloc(sizeof(MenuComponent)); + + menu->window_width = window->width * window->scale; + menu->visible = false; + + menu->items = linked_list_create(false); + menu->highlight_item = NULL; + + menu->renderer = window->sdl_context.renderer; + menu->font = font; + CREATE_PIXEL_TEXTURE(background_texture, menu, MENU_BACKGROUND_COLOR); + CREATE_PIXEL_TEXTURE(highlight_texture, menu, MENU_HIGHLIGHT_COLOR); + + return menu; +} + +MenuItemComponent *menu_item_create(char *label, on_click_callback on_click) { + MenuItemComponent *menu_item = malloc(sizeof(MenuItemComponent)); + + menu_item->label = label; + menu_item->on_click = on_click; + menu_item->sub_items = linked_list_create(false); + menu_item->is_highlighted = false; + + return menu_item; +} + +void menu_append(MenuComponent *menu, MenuItemComponent *menu_item) { + LinkedList *menu_items = &menu->items; + linked_list_add(menu_items, menu_item); +} + +void menu_item_append(MenuItemComponent *menu_item, MenuItemComponent *sub_item) { + LinkedList *sub_items = &menu_item->sub_items; + linked_list_add(sub_items, sub_item); +} + +void menu_item_build(MenuItemComponent *menu_item, SDL_Renderer *renderer, TTF_Font *font, bool top_level) { + SDL_Color label_color = MENU_TEXT_COLOR; + int base_x = menu_item->collision_rect.x; + int base_y = MENU_HEIGHT + MENU_ITEM_MARGIN_Y; + + if (!top_level) { + base_x += menu_item->collision_rect.w; + base_y = menu_item->collision_rect.y + MENU_ITEM_MARGIN_Y; + } + + FOR_EACH_SUBITEM(menu_item) + SDL_Surface *label_surface = TTF_RenderText_Solid(font, item->label, label_color); + + item->label_texture = SDL_CreateTextureFromSurface(renderer, label_surface); + SDL_Rect draw_rect = {base_x + MENU_ITEM_MARGIN_X, base_y, label_surface->w, label_surface->h}; + item->draw_rect = draw_rect; + + SDL_Rect menu_item_col = {base_x, base_y - MENU_ITEM_MARGIN_Y, label_surface->w + MENU_ITEM_MARGIN_X * 2, + MENU_HEIGHT}; + item->collision_rect = menu_item_col; + base_y += item->draw_rect.h + MENU_ITEM_MARGIN_Y * 2; + + SDL_FreeSurface(label_surface); + + menu_item_build(item, renderer, font, false); + END_FOR_EACH_ITEM +} + +void menu_build(MenuComponent *menu) { + assert(menu->font != NULL); + + SDL_Color label_color = MENU_TEXT_COLOR; + int next_x = MENU_ITEM_MARGIN_X; + FOR_EACH_ITEM(menu) + SDL_Surface *label_surface = TTF_RenderText_Solid(menu->font, item->label, label_color); + + item->label_texture = SDL_CreateTextureFromSurface(menu->renderer, label_surface); + SDL_Rect draw_rect = {next_x, MENU_ITEM_MARGIN_Y, label_surface->w, label_surface->h}; + item->draw_rect = draw_rect; + + SDL_Rect menu_item_col = {next_x - MENU_ITEM_MARGIN_X, 0, label_surface->w + MENU_ITEM_MARGIN_X * 2, + MENU_HEIGHT}; + item->collision_rect = menu_item_col; + next_x += item->draw_rect.w + MENU_ITEM_MARGIN_X * 2; + + SDL_FreeSurface(label_surface); + + menu_item_build(item, menu->renderer, menu->font, true); + END_FOR_EACH_ITEM +} + +void menu_item_render(MenuComponent *menu, MenuItemComponent *menu_item) { + SDL_Texture *back_texture = menu_item->is_highlighted ? menu->highlight_texture : menu->background_texture; + SDL_RenderCopy(menu->renderer, back_texture, NULL, &menu_item->collision_rect); + SDL_RenderCopy(menu->renderer, menu_item->label_texture, NULL, &menu_item->draw_rect); + + if (!menu_item->is_highlighted) { + return; + } + + FOR_EACH_SUBITEM(menu_item) + menu_item_render(menu, item); + END_FOR_EACH_ITEM +} + +void menu_render(MenuComponent *menu) { + if (!menu->visible) { + return; + } + + int display_width = menu->window_width; + int display_height = MENU_HEIGHT; + SDL_Rect menu_rect = {0, 0, display_width, display_height}; + SDL_RenderCopy(menu->renderer, menu->background_texture, NULL, &menu_rect); + + FOR_EACH_ITEM(menu) + menu_item_render(menu, item); + END_FOR_EACH_ITEM +} + +void menu_item_list_destroy(LinkedList *list) { + MenuItemComponent *menu_item; + LinkedListCursor cursor = menu_create_item_cursor(list, &menu_item); + while (menu_item != NULL) { + menu_item_destroy(menu_item); + + menu_item = menu_get_next_item(&cursor); + } + + linked_list_destroy(list); +} + +void menu_destroy(MenuComponent *menu) { + menu_item_list_destroy(&menu->items); + + SDL_DestroyTexture(menu->background_texture); + free(menu); +} + +void menu_item_destroy(MenuItemComponent *menu_item) { + SDL_DestroyTexture(menu_item->label_texture); + + menu_item_list_destroy(&menu_item->sub_items); + free(menu_item); +} + +/** + * Checks if a point is over a menu item. + * This function will return true if the point is in an item or one of its sub-items. + */ +void menu_item_hover(MenuComponent *menu, MenuItemComponent *menu_item, SDL_Point *pos) { + if (SDL_PointInRect(pos, &menu_item->collision_rect)) { + menu_item->is_highlighted = true; + menu->highlight_item = menu_item; + } else if (menu_item->is_highlighted) { // We can't hover a sub-item if this item is not highlighted because it is not visible + bool any_sub_highlighted = false; + FOR_EACH_SUBITEM(menu_item) + menu_item_hover(menu, item, pos); + + if (item->is_highlighted) { + any_sub_highlighted = true; + } + END_FOR_EACH_ITEM + + // If any sub-item is highlighted, the current item also is + menu_item->is_highlighted = any_sub_highlighted; + } +} + +bool menu_mouse_motion(MenuComponent *menu, int x, int y) { + menu->visible = y <= MENU_VISIBLE_HEIGHT; + SDL_Point pos = {x, y}; + + menu->highlight_item = NULL; + + FOR_EACH_ITEM(menu) + menu_item_hover(menu, item, &pos); + END_FOR_EACH_ITEM + + return menu->highlight_item != NULL; +} + +bool menu_mouse_click(MenuComponent *menu) { + if (menu->highlight_item == NULL) { + return false; + } + + log_info("%s", menu->highlight_item->label); + return true; +} + +Component *menu_as_component(MenuComponent *menu) { + Component *component = malloc(sizeof(Component)); + + component->ref = menu; + component->render = (void (*)(void *)) &menu_render; + component->destroy = (void (*)(void *)) &menu_destroy; + component->mouse_motion = (bool (*)(void *, int, int)) &menu_mouse_motion; + component->mouse_click = (bool (*)(void *)) &menu_mouse_click; + + return component; +} \ No newline at end of file diff --git a/gui/components/window_menu.h b/gui/components/window_menu.h new file mode 100644 index 0000000..1b33715 --- /dev/null +++ b/gui/components/window_menu.h @@ -0,0 +1,110 @@ +// +// Created by william on 8/23/24. +// + +#ifndef NES_EMULATOR_WINDOW_MENU_H +#define NES_EMULATOR_WINDOW_MENU_H + +#include "window.h" +#include "linked_list.h" +#include "component.h" + +#define MENU_HEIGHT 24 +#define MENU_VISIBLE_HEIGHT 64; +#define MENU_BACKGROUND_COLOR 0xff353535 +#define MENU_HIGHLIGHT_COLOR 0xff4d4d4d +#define MENU_TEXT_COLOR {0xff, 0xff, 0xff} +#define MENU_ITEM_MARGIN_X 20 +#define MENU_ITEM_MARGIN_Y 4 + +typedef void (*on_click_callback)(); + +typedef struct menu_item_component { + char *label; + on_click_callback on_click; + + bool is_highlighted; + + LinkedList sub_items; + SDL_Texture *label_texture; + SDL_Rect draw_rect; + SDL_Rect collision_rect; +} MenuItemComponent; + +typedef struct menu_component { + int window_width; + bool visible; + + LinkedList items; + MenuItemComponent *highlight_item; + + SDL_Renderer *renderer; + TTF_Font *font; + SDL_Texture *background_texture; + SDL_Texture *highlight_texture; +} MenuComponent; + +/** + * Creates a menu fow a window. + * @param window A reference to the window + * @param font A reference to the TTF font to use to render text + * @return A reference to the menu component + */ +MenuComponent *menu_create(Window *window, TTF_Font *font); + +/** + * Creates a menu item. Can be configured with a callback function which will be called when the menu item is clicked. + * Note that the callback function will be overridden if the menu item has sub items. + * @param label The label of the menu item + * @param on_click The callback function to call when clicked + * @return A reference to the menu item + */ +MenuItemComponent *menu_item_create(char *label, on_click_callback on_click); + +/** + * Adds an item to a menu. + * @param menu A reference to the menu + * @param menu_item A reference to the menu item to add to the menu + */ +void menu_append(MenuComponent *menu, MenuItemComponent *menu_item); + +/** + * Adds an sub-item to a menu item. + * Note that this will have the effect of preventing the callback to be called. + * @param menu_item A reference to the menu item + * @param sub_item A reference to the sub item to add to the menu item + */ +void menu_item_append(MenuItemComponent *menu_item, MenuItemComponent *sub_item); + +/** + * Builds the ressources needed for a menu to be displayed. (ex: textures) + * @param menu The menu to build + */ +void menu_build(MenuComponent *menu); + +/** + * Renders a menu to its window. + * @param menu A reference to the menu to render + */ +void menu_render(MenuComponent *menu); + +/** + * Destroys a menu, freeing its memory. + * @param menu A reference to the menu to destroy + */ +void menu_destroy(MenuComponent *menu); + +/** + * Destroys a menu item to free its memory. + * @param menu_item A reference to the menu item to destroy + */ +void menu_item_destroy(MenuItemComponent *menu_item); + +/** + * Returns a menu as a component. + * @param menu The menu + * @return + */ +Component *menu_as_component(MenuComponent *menu); + +#endif //NES_EMULATOR_WINDOW_MENU_H diff --git a/gui/gui.c b/gui/gui.c index 823c8ee..49b3a9c 100644 --- a/gui/gui.c +++ b/gui/gui.c @@ -14,6 +14,12 @@ #include "char_map.h" #include "dbg_palette.h" +#if DEBUG +#define WINDOW_ID_MAIN 3 +#else +#define WINDOW_ID_MAIN 1 +#endif + typedef struct nes_gui { NesMainWindow main_window; NesPatternWindow pattern_window; @@ -43,7 +49,7 @@ bool gui_init() { pattern_window_init(&gui.pattern_window); nametable_window_init(&gui.nametable_window); - char_map_init(gui.main_window.sdl_context.renderer, gui.font); + char_map_init(gui.main_window.window.sdl_context.renderer, gui.font); #endif main_window_init(&gui.main_window); @@ -70,7 +76,7 @@ void gui_post_sysinit() { dbg_pattern_table_init(); dbg_nametable_init(); - // TODO: The texture is rendered before the palette data is in the PPU memory, so the only color is grey + // TODO: The background_texture is rendered before the palette data is in the PPU memory, so the only color is grey pattern_window_build_table(&gui.pattern_window); nametable_window_update(&gui.nametable_window); #endif @@ -103,21 +109,34 @@ int gui_input() { system_toggle_pause(lctrl); break; #if DEBUG - case SDLK_t: - ppu_debug->flags.tile_debugger = !ppu_debug->flags.tile_debugger; - break; - case SDLK_n: - ppu_debug->flags.tile_debugger_pattern_half = (ppu_debug->flags.tile_debugger_pattern_half + 1) % 3; - break; - default: - pattern_window_key_up(&gui.pattern_window, event.key.keysym.sym); - break; -#else - default: + case SDLK_t: + ppu_debug->flags.tile_debugger = !ppu_debug->flags.tile_debugger; break; + case SDLK_n: + ppu_debug->flags.tile_debugger_pattern_half = (ppu_debug->flags.tile_debugger_pattern_half + 1) % 3; + break; + default: + pattern_window_key_up(&gui.pattern_window, event.key.keysym.sym); + break; +#else + default: + break; #endif } } + + if (event.type == SDL_MOUSEMOTION) { + int x = event.motion.x; + int y = event.motion.y; + + if (event.window.windowID == WINDOW_ID_MAIN) { + main_window_mouse_motion(&gui.main_window, x, y); + } + } + + if (event.type == SDL_MOUSEBUTTONUP && event.window.windowID == WINDOW_ID_MAIN) { + main_window_mouse_click(&gui.main_window); + } } return 1; @@ -142,8 +161,6 @@ void gui_present() { pattern_window_present(&gui.pattern_window); nametable_window_present(&gui.nametable_window); #endif - - main_window_present(&gui.main_window); } void gui_delay() { @@ -159,6 +176,10 @@ void gui_delay() { gui.last_frame_tick = SDL_GetTicks(); } +TTF_Font *gui_get_font() { + return gui.font; +} + unsigned int gui_get_frame_delay() { return gui.frame_delay; } \ No newline at end of file diff --git a/gui/gui.h b/gui/gui.h index 608e4e0..6c0a96b 100644 --- a/gui/gui.h +++ b/gui/gui.h @@ -6,6 +6,7 @@ #define NES_EMULATOR_GUI_H #include +#include bool gui_init(); void gui_uninit(); @@ -17,6 +18,7 @@ void gui_render(); void gui_present(); void gui_delay(); +TTF_Font* gui_get_font(); unsigned int gui_get_frame_delay(); #endif //NES_EMULATOR_GUI_H diff --git a/gui/main_window.c b/gui/main_window.c index 063d103..2b824c7 100644 --- a/gui/main_window.c +++ b/gui/main_window.c @@ -7,19 +7,37 @@ #include "log.h" #include "char_map.h" #include "gui.h" +#include "components/window_menu.h" void main_window_init(NesMainWindow *window) { - window->sdl_context = window_init("NES Emulator", MAIN_WINDOW_WIDTH, MAIN_WINDOW_HEIGHT, MAIN_WINDOW_SCALE); - window->texture = SDL_CreateTexture(window->sdl_context.renderer, SDL_PIXELFORMAT_ARGB8888, - SDL_TEXTUREACCESS_STREAMING, MAIN_WINDOW_WIDTH, MAIN_WINDOW_HEIGHT); + window->window = window_create("NES Emulator", MAIN_WINDOW_WIDTH, MAIN_WINDOW_HEIGHT, MAIN_WINDOW_SCALE); + window->texture = window_create_texture(&window->window, SDL_TEXTUREACCESS_STREAMING); + + MenuComponent *menu = menu_create(&window->window, gui_get_font()); + + MenuItemComponent *mi_file = menu_item_create("FILE", NULL); + menu_append(menu, mi_file); + MenuItemComponent *mi_file_open = menu_item_create("OPEN ROM...", NULL); + menu_item_append(mi_file, mi_file_open); + + MenuItemComponent *mi_debug = menu_item_create("DEBUG", NULL); + menu_append(menu, mi_debug); + MenuItemComponent *mi_debug_nametable = menu_item_create("NAMETABLE", NULL); + menu_item_append(mi_debug, mi_debug_nametable); + MenuItemComponent *mi_debug_pattern = menu_item_create("PATTERN TABLE", NULL); + menu_item_append(mi_debug, mi_debug_pattern); + + menu_build(menu); + window_add_component(&window->window, menu_as_component(menu)); } void main_window_uninit(NesMainWindow *window) { SDL_DestroyTexture(window->texture); - window_uninit(window->sdl_context); + window_destroy(&window->window); } #if DEBUG + void main_window_render_delay(SDL_Renderer *renderer) { Uint32 delay = gui_get_frame_delay(); @@ -32,18 +50,22 @@ void main_window_render_delay(SDL_Renderer *renderer) { char_map_render(renderer, buffer); } + #endif void main_window_render(NesMainWindow *window, pixel *pixels) { - SDL_RenderClear(window->sdl_context.renderer); SDL_UpdateTexture(window->texture, NULL, pixels, 256 * sizeof(pixel)); - SDL_RenderCopy(window->sdl_context.renderer, window->texture, NULL, NULL); -#if DEBUG - main_window_render_delay(window->sdl_context.renderer); -#endif + window_render_texture(&window->window, window->texture); + window_render_components(&window->window); + + window_present(&window->window); } -void main_window_present(NesMainWindow *window) { - SDL_RenderPresent(window->sdl_context.renderer); +void main_window_mouse_motion(NesMainWindow *window, int x, int y) { + window_mouse_motion(&window->window, x, y); +} + +void main_window_mouse_click(NesMainWindow *window) { + window_mouse_click(&window->window); } \ No newline at end of file diff --git a/gui/main_window.h b/gui/main_window.h index 57ce4e3..60a08be 100644 --- a/gui/main_window.h +++ b/gui/main_window.h @@ -7,14 +7,14 @@ #include #include "../include/ppu.h" -#include "window.h" +#include "components/window.h" #define MAIN_WINDOW_WIDTH 256 #define MAIN_WINDOW_HEIGHT 240 #define MAIN_WINDOW_SCALE 3 typedef struct nes_main_window { - NesSdlContext sdl_context; + Window window; SDL_Texture *texture; } NesMainWindow; @@ -23,10 +23,7 @@ void main_window_init(NesMainWindow *window); void main_window_uninit(NesMainWindow *window); void main_window_render(NesMainWindow *window, pixel* pixels); -void main_window_present(NesMainWindow *window); - -#if DEBUG -void main_window_render_delay(SDL_Renderer *renderer); -#endif +void main_window_mouse_motion(NesMainWindow *window, int x, int y); +void main_window_mouse_click(NesMainWindow *window); #endif //NES_EMULATOR_MAIN_WINDOW_H diff --git a/gui/nametable_window.c b/gui/nametable_window.c index 531ddf5..1d3189e 100644 --- a/gui/nametable_window.c +++ b/gui/nametable_window.c @@ -10,15 +10,13 @@ #define NW_BUFFER_SIZE (NAMETABLE_ROW_WIDTH * NAMETABLE_COL_HEIGHT * PATTERN_DRAW_SIZE * PATTERN_DRAW_SIZE) void nametable_window_init(NesNametableWindow *window) { - window->sdl_context = window_init("Nametable", NW_WIDTH, NW_HEIGHT, NW_SCALE); - - window->texture = SDL_CreateTexture(window->sdl_context.renderer, SDL_PIXELFORMAT_ARGB8888, - SDL_TEXTUREACCESS_STREAMING, NW_WIDTH, NW_HEIGHT); + window->window = window_create("Nametable", NW_WIDTH, NW_HEIGHT, NW_SCALE); + window->texture = window_create_texture(&window->window, SDL_TEXTUREACCESS_STREAMING); } void nametable_window_uninit(NesNametableWindow *window) { SDL_DestroyTexture(window->texture); - window_uninit(window->sdl_context); + window_destroy(&window->window); } void nametable_window_update_bank(NesNametableWindow *window, int bank, pixel *buffer) { @@ -44,10 +42,9 @@ void nametable_window_update(NesNametableWindow *window) { } void nametable_window_render(NesNametableWindow *window) { - SDL_RenderClear(window->sdl_context.renderer); - SDL_RenderCopy(window->sdl_context.renderer, window->texture, NULL, NULL); + window_render_texture(&window->window, window->texture); } void nametable_window_present(NesNametableWindow *window) { - SDL_RenderPresent(window->sdl_context.renderer); + window_present(&window->window); } \ No newline at end of file diff --git a/gui/nametable_window.h b/gui/nametable_window.h index 3063c89..1233c22 100644 --- a/gui/nametable_window.h +++ b/gui/nametable_window.h @@ -5,7 +5,7 @@ #ifndef NES_EMULATOR_NAMETABLE_WINDOW_H #define NES_EMULATOR_NAMETABLE_WINDOW_H -#include "window.h" +#include "components/window.h" #include "../include/types.h" #define NW_SCALE 1 @@ -13,16 +13,18 @@ #define NW_ROW_TILE_COUNT 64 typedef struct nes_nametable_window { - NesSdlContext sdl_context; + Window window; SDL_Texture *texture; } NesNametableWindow; void nametable_window_init(NesNametableWindow *window); + void nametable_window_uninit(NesNametableWindow *window); void nametable_window_update(NesNametableWindow *window); void nametable_window_render(NesNametableWindow *window); + void nametable_window_present(NesNametableWindow *window); #endif //NES_EMULATOR_NAMETABLE_WINDOW_H diff --git a/gui/pattern_window.c b/gui/pattern_window.c index e87a137..c1043fc 100644 --- a/gui/pattern_window.c +++ b/gui/pattern_window.c @@ -10,16 +10,14 @@ #define PW_BUFFER_SIZE (PW_WIDTH * PW_HEIGHT) void pattern_window_init(NesPatternWindow *window) { - window->sdl_context = window_init("Pattern Table", PW_WIDTH, PW_HEIGHT, PW_SCALE); - - window->texture = SDL_CreateTexture(window->sdl_context.renderer, SDL_PIXELFORMAT_ARGB8888, - SDL_TEXTUREACCESS_STATIC, PW_WIDTH, PW_HEIGHT); + window->window = window_create("Pattern Table", PW_WIDTH, PW_HEIGHT, PW_SCALE); + window->texture = window_create_texture(&window->window, SDL_TEXTUREACCESS_STATIC); window->palette = 0; } void pattern_window_uninit(NesPatternWindow *window) { SDL_DestroyTexture(window->texture); - window_uninit(window->sdl_context); + window_destroy(&window->window); } void pattern_window_build_table(NesPatternWindow *window) { @@ -44,10 +42,9 @@ void pattern_window_key_up(NesPatternWindow *window, SDL_KeyCode keycode) { } void pattern_window_render(NesPatternWindow *window) { - SDL_RenderClear(window->sdl_context.renderer); - SDL_RenderCopy(window->sdl_context.renderer, window->texture, NULL, NULL); + window_render_texture(&window->window, window->texture); } void pattern_window_present(NesPatternWindow *window) { - SDL_RenderPresent(window->sdl_context.renderer); + window_present(&window->window); } \ No newline at end of file diff --git a/gui/pattern_window.h b/gui/pattern_window.h index 88f6550..caa8349 100644 --- a/gui/pattern_window.h +++ b/gui/pattern_window.h @@ -6,14 +6,14 @@ #define NES_EMULATOR_PATTERN_WINDOW_H #include "../include/types.h" -#include "window.h" +#include "components/window.h" #define PW_SCALE 2 #define PW_ROW_TILE_COUNT 16 #define PW_PALETTE_MAX 3 typedef struct nes_pattern_window { - NesSdlContext sdl_context; + Window window; SDL_Texture *texture; byte palette; } NesPatternWindow; diff --git a/gui/window.c b/gui/window.c deleted file mode 100644 index 4852d85..0000000 --- a/gui/window.c +++ /dev/null @@ -1,101 +0,0 @@ -// -// Created by william on 17/05/24. -// - -#include -#include "window.h" -#include "log.h" - -NesSdlContext window_init(char *title, int width, int height, int scale) { - NesSdlContext 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); - } - - return context; -} - -void window_uninit(NesSdlContext context) { - SDL_DestroyRenderer(context.renderer); - SDL_DestroyWindow(context.window); -} - -//NesWindow window_init(int width, int height, char *title) { -// NesWindow win; -// win.width = width; -// win.height = height; -// -// 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); -// } -// -// win.window = SDL_CreateWindow(title, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, win.width, win.height, -// window_flags); -// if (!win.window) { -// log_error("Failed to open %d x %d sdl_window: %s", win.width, win.height, SDL_GetError()); -// exit(-1); -// } -// -// win.renderer = SDL_CreateRenderer(win.window, -1, renderer_flags); -// if (!win.renderer) { -// log_error("Failed to create renderer: %s\n", SDL_GetError()); -// exit(-1); -// } -// -// return win; -//} -// -//void window_uninit(NesWindow *window) { -// SDL_DestroyRenderer(window->renderer); -// SDL_DestroyWindow(window->window); -//} -// -//void window_render(NesWindow *window) { -// SDL_RenderClear(window->renderer); -// -//// for (int y = 0; y < window->canvas.height; y++) { -//// for (int x = 0; x < window->canvas.width; x++) { -//// int pixel_index = x + y * window->canvas.width; -//// Pixel pixel = window->canvas.pixels[pixel_index]; -//// -//// SDL_SetRenderDrawColor(window->renderer, pixel.r, pixel.g, pixel.b, 255); -//// -//// for (int i = 0; i < window->scaling; i++) { -//// for (int j = 0; j < window->scaling; j++) { -//// int scaled_x = x * window->scaling + i; -//// int scaled_y = y * window->scaling + j; -//// SDL_RenderDrawPoint(window->renderer, scaled_x, scaled_y); -//// } -//// } -//// } -//// } -//} -// -//void window_present(NesWindow *window) { -// SDL_RenderPresent(window->renderer); -//} \ No newline at end of file diff --git a/gui/window.h b/gui/window.h deleted file mode 100644 index 7392e27..0000000 --- a/gui/window.h +++ /dev/null @@ -1,18 +0,0 @@ -// -// Created by william on 17/05/24. -// - -#ifndef NES_EMULATOR_WINDOW_H -#define NES_EMULATOR_WINDOW_H - -#include - -typedef struct nes_sdl_context { - SDL_Renderer *renderer; - SDL_Window *window; -} NesSdlContext; - -NesSdlContext window_init(char *title, int width, int height, int scale); -void window_uninit(NesSdlContext context); - -#endif //NES_EMULATOR_WINDOW_H \ No newline at end of file diff --git a/main.c b/main.c index 536f926..125b706 100644 --- a/main.c +++ b/main.c @@ -50,7 +50,7 @@ FILE *add_log_file(char *name, int level) { } void init_logging() { - log_set_level(LOG_DEBUG); + log_set_level(LOG_INFO); // Print current working directory char cwd[256]; @@ -81,7 +81,10 @@ void init_logging() { void close_logging() { fclose(log_files.info); + +#if DEBUG fclose(log_files.debug); +#endif } int main() { diff --git a/utils/CMakeLists.txt b/utils/CMakeLists.txt deleted file mode 100644 index 7806562..0000000 --- a/utils/CMakeLists.txt +++ /dev/null @@ -1,4 +0,0 @@ -set(HEADERS linked_list.h) -set(SOURCE linked_list.c) - -add_library(nes_utils ${HEADERS} ${SOURCE}) \ No newline at end of file diff --git a/utils/linked_list.c b/utils/linked_list.c deleted file mode 100644 index 0955ae6..0000000 --- a/utils/linked_list.c +++ /dev/null @@ -1,126 +0,0 @@ -// -// Created by william on 1/16/24. -// - -#include -#include -#include -#include -#include "linked_list.h" - -LinkedList linked_list_init(bool circular) { - LinkedList list; - - list.circular = circular; - list.size = 0; - list.head = NULL; - list.end = NULL; - list.current = NULL; - - return list; -} - -void linked_list_add(LinkedList *list, void *data) { - assert(list != NULL); - - LinkedListNode *node = malloc(sizeof(LinkedListNode)); - if (node == NULL) { - perror("Failed to allocate memory for linked list node"); - exit(EXIT_FAILURE); - } - - node->data = data; - node->previous = list->end; - - if (list->head == NULL) { - list->head = node; - list->current = node; - } - - if (list->end != NULL) { - list->end->next = node; - } - - if (list->circular) { - node->next = list->head; - } else { - node->next = NULL; - } - - list->end = node; - list->size++; -} - -LinkedListNode *linked_list_next(LinkedList *list) { - assert(list != NULL); - - if (list->head == NULL) { - return NULL; - } - - LinkedListNode *next = list->current->next; - list->current = next; - return next; -} - -void linked_list_cursor_reset(LinkedList *list) { - assert(list != NULL); - - list->current = list->head; -} - -LinkedListNode *linked_list_get_if(LinkedList *list, bool(*predicate)(void *, void *), void *userdata) { - assert(list != NULL); - assert(predicate != NULL); - - LinkedListNode *node = list->head; - - while (node != NULL) { - if (predicate(node->data, userdata)) { - return node; - } - - node = node->next; - } - - return NULL; -} - -LinkedListNode *linked_list_get_near(LinkedList *list, int(*compute_distance)(void *, void *), void *userdata) { - assert(list != NULL); - assert(compute_distance != NULL); - - LinkedListNode *near_node = list->head; - - int current_distance = 0x7fffffff; - while (near_node->next != NULL && current_distance != 0) { - int next_distance = compute_distance(near_node->next->data, userdata); - if (next_distance > current_distance) { - break; - } - - near_node = near_node->next; - current_distance = next_distance; - } - - // After the loop, we have found the nearest node in the list, assuming there is only one point of convergence - return near_node; -} - -void linked_list_uninit(LinkedList *list) { - assert(list != NULL); - - LinkedListNode *node = list->head; - while (node != NULL) { - LinkedListNode *current_node = node; - node = node->next; - - free(current_node->data); - free(current_node); - - if (node == list->head) { - // The list may be circular, we don't want an infinite free loop - break; - } - } -} \ No newline at end of file diff --git a/utils/linked_list.h b/utils/linked_list.h deleted file mode 100644 index 6e293d3..0000000 --- a/utils/linked_list.h +++ /dev/null @@ -1,82 +0,0 @@ -// -// Created by william on 1/16/24. -// - -#include - -#ifndef NESEMULATOR_LINKED_LIST_H -#define NESEMULATOR_LINKED_LIST_H - -typedef struct linked_list_node { - struct linked_list_node *previous; - struct linked_list_node *next; - void *data; -} LinkedListNode; - -typedef struct linked_list { - bool circular; - unsigned int size; - LinkedListNode *head; - LinkedListNode *end; - LinkedListNode *current; -} LinkedList; - -/** - * Initializes a new linked list. - * - * @param circular If the list is circular, meaning that the last node is linked to the first node. - * @return The linked list instance - */ -LinkedList linked_list_init(bool circular); - -/** - * Adds data to a linked list. - * - * @param list The linked list - * @param data The data to add - */ -void linked_list_add(LinkedList *list, void *data); - -/** - * Gets the next node in the list. - * - * @param list The linked list - * @return The next node in the list. Can be NULL if the list is empty or depleted. - */ -LinkedListNode *linked_list_next(LinkedList *list); - -/** - * Resets the position of the cursor to the head of the list. - */ -void linked_list_cursor_reset(LinkedList *list); - -/** - * Searches for data corresponding to a predicate. - * The search will stop after reaching the first node matching the given predicate. - * - * @param list The list to search in - * @param predicate The predicate to match the data - * @param userdata Parameter to pass to the predicate - * @return The first node in the list matching the predicate - */ -LinkedListNode *linked_list_get_if(LinkedList *list, bool(*predicate)(void *, void *), void *userdata); - -/** - * Searches for data with the smallest distance computed from a function. - * The search will stop when a node increasing the distance is found. For this reason, the distance computing function should have a single minimum. - * - * @param list The list to search in - * @param compute_distance The function to compute the distance of a node's data - * @param userdata Parameter to pass to the function - * @return The node with the smallest distance - */ -LinkedListNode *linked_list_get_near(LinkedList *list, int(*compute_distance)(void *, void *), void *userdata); - -/** - * Deinitializes a linked list. - * - * @param list The list to deinitialize - */ -void linked_list_uninit(LinkedList *list); - -#endif //NESEMULATOR_LINKED_LIST_H