SDL menu in top of main window
This commit is contained in:
parent
32c9cebd19
commit
f18ad715fb
|
@ -0,0 +1,8 @@
|
||||||
|
<component name="InspectionProjectProfileManager">
|
||||||
|
<profile version="1.0">
|
||||||
|
<option name="myName" value="Project Default" />
|
||||||
|
<inspection_tool class="ClangTidy" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<option name="clangTidyChecks" value="-*,bugprone-argument-comment,bugprone-assert-side-effect,bugprone-bad-signal-to-kill-thread,bugprone-branch-clone,bugprone-copy-constructor-init,bugprone-dangling-handle,bugprone-dynamic-static-initializers,bugprone-fold-init-type,bugprone-forward-declaration-namespace,bugprone-forwarding-reference-overload,bugprone-inaccurate-erase,bugprone-incorrect-roundings,bugprone-integer-division,bugprone-lambda-function-name,bugprone-macro-parentheses,bugprone-macro-repeated-side-effects,bugprone-misplaced-operator-in-strlen-in-alloc,bugprone-misplaced-pointer-arithmetic-in-alloc,bugprone-misplaced-widening-cast,bugprone-move-forwarding-reference,bugprone-multiple-statement-macro,bugprone-no-escape,bugprone-parent-virtual-call,bugprone-posix-return,bugprone-reserved-identifier,bugprone-sizeof-container,bugprone-sizeof-expression,bugprone-spuriously-wake-up-functions,bugprone-string-constructor,bugprone-string-integer-assignment,bugprone-string-literal-with-embedded-nul,bugprone-suspicious-enum-usage,bugprone-suspicious-include,bugprone-suspicious-memset-usage,bugprone-suspicious-missing-comma,bugprone-suspicious-semicolon,bugprone-suspicious-string-compare,bugprone-suspicious-memory-comparison,bugprone-suspicious-realloc-usage,bugprone-swapped-arguments,bugprone-terminating-continue,bugprone-throw-keyword-missing,bugprone-too-small-loop-variable,bugprone-undefined-memory-manipulation,bugprone-undelegated-constructor,bugprone-unhandled-self-assignment,bugprone-unused-raii,bugprone-unused-return-value,bugprone-use-after-move,bugprone-virtual-near-miss,cert-dcl21-cpp,cert-dcl58-cpp,cert-err34-c,cert-err52-cpp,cert-err60-cpp,cert-flp30-c,cert-msc50-cpp,cert-msc51-cpp,cert-str34-c,cppcoreguidelines-interfaces-global-init,cppcoreguidelines-narrowing-conversions,cppcoreguidelines-pro-type-member-init,cppcoreguidelines-pro-type-static-cast-downcast,cppcoreguidelines-slicing,google-default-arguments,google-explicit-constructor,google-runtime-operator,hicpp-exception-baseclass,hicpp-multiway-paths-covered,misc-misplaced-const,misc-new-delete-overloads,misc-non-copyable-objects,misc-throw-by-value-catch-by-reference,misc-unconventional-assign-operator,misc-uniqueptr-reset-release,modernize-avoid-bind,modernize-concat-nested-namespaces,modernize-deprecated-headers,modernize-deprecated-ios-base-aliases,modernize-loop-convert,modernize-make-shared,modernize-make-unique,modernize-pass-by-value,modernize-raw-string-literal,modernize-redundant-void-arg,modernize-replace-auto-ptr,modernize-replace-disallow-copy-and-assign-macro,modernize-replace-random-shuffle,modernize-return-braced-init-list,modernize-shrink-to-fit,modernize-unary-static-assert,modernize-use-auto,modernize-use-bool-literals,modernize-use-emplace,modernize-use-equals-default,modernize-use-equals-delete,modernize-use-nodiscard,modernize-use-noexcept,modernize-use-nullptr,modernize-use-override,modernize-use-transparent-functors,modernize-use-uncaught-exceptions,mpi-buffer-deref,mpi-type-mismatch,openmp-use-default-none,performance-faster-string-find,performance-for-range-copy,performance-implicit-conversion-in-loop,performance-inefficient-algorithm,performance-inefficient-string-concatenation,performance-inefficient-vector-operation,performance-move-const-arg,performance-move-constructor-init,performance-no-automatic-move,performance-noexcept-move-constructor,performance-trivially-destructible,performance-type-promotion-in-math-fn,performance-unnecessary-copy-initialization,performance-unnecessary-value-param,portability-simd-intrinsics,readability-avoid-const-params-in-decls,readability-const-return-type,readability-container-size-empty,readability-convert-member-functions-to-static,readability-delete-null-pointer,readability-deleted-default,readability-inconsistent-declaration-parameter-name,readability-make-member-function-const,readability-misleading-indentation,readability-misplaced-array-index,readability-non-const-parameter,readability-redundant-control-flow,readability-redundant-declaration,readability-redundant-function-ptr-dereference,readability-redundant-smartptr-get,readability-redundant-string-cstr,readability-redundant-string-init,readability-simplify-subscript-expr,readability-static-accessed-through-instance,readability-static-definition-in-anonymous-namespace,readability-string-compare,readability-uniqueptr-delete-release,readability-use-anyofallof" />
|
||||||
|
</inspection_tool>
|
||||||
|
</profile>
|
||||||
|
</component>
|
|
@ -15,7 +15,6 @@ add_subdirectory(cpu)
|
||||||
add_subdirectory(ppu)
|
add_subdirectory(ppu)
|
||||||
add_subdirectory(mappers)
|
add_subdirectory(mappers)
|
||||||
add_subdirectory(rom)
|
add_subdirectory(rom)
|
||||||
add_subdirectory(utils)
|
|
||||||
add_subdirectory(gui)
|
add_subdirectory(gui)
|
||||||
|
|
||||||
list(APPEND EXTRA_INCLUDES
|
list(APPEND EXTRA_INCLUDES
|
||||||
|
@ -23,8 +22,6 @@ list(APPEND EXTRA_INCLUDES
|
||||||
"${PROJECT_SOURCE_DIR}/ppu"
|
"${PROJECT_SOURCE_DIR}/ppu"
|
||||||
"${PROJECT_SOURCE_DIR}/mappers"
|
"${PROJECT_SOURCE_DIR}/mappers"
|
||||||
"${PROJECT_SOURCE_DIR}/rom"
|
"${PROJECT_SOURCE_DIR}/rom"
|
||||||
"${PROJECT_SOURCE_DIR}/debugger"
|
|
||||||
"${PROJECT_SOURCE_DIR}/utils"
|
|
||||||
"${PROJECT_SOURCE_DIR}/gui")
|
"${PROJECT_SOURCE_DIR}/gui")
|
||||||
|
|
||||||
set(HEADERS include/system.h include/types.h)
|
set(HEADERS include/system.h include/types.h)
|
||||||
|
@ -32,7 +29,7 @@ set(SOURCE main.c system.c)
|
||||||
|
|
||||||
add_executable(nes_emulator ${HEADERS} ${SOURCE})
|
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
|
target_include_directories(nes_emulator PUBLIC
|
||||||
"${PROJECT_BINARY_DIR}"
|
"${PROJECT_BINARY_DIR}"
|
||||||
${EXTRA_INCLUDES})
|
${EXTRA_INCLUDES})
|
|
@ -29,7 +29,7 @@ only tested on Linux. Here is how to run the project:
|
||||||
- PPU
|
- PPU
|
||||||
- Registers: Done
|
- Registers: Done
|
||||||
- VRAM: Done
|
- VRAM: Done
|
||||||
- Background rendering: In Progress
|
- Background rendering: Done
|
||||||
- Sprite rendering: To Do
|
- Sprite rendering: To Do
|
||||||
- Input: To Do
|
- Input: To Do
|
||||||
- APU: To Do
|
- APU: To Do
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
#include "../cpu/decoding.h"
|
#include "../cpu/decoding.h"
|
||||||
#include "cursor.h"
|
#include "cursor.h"
|
||||||
#include "window.h"
|
#include "window.h"
|
||||||
#include "../utils/linked_list.h"
|
#include "components/linked_list.h"
|
||||||
|
|
||||||
#define PROGRAM_VIEW_HEIGHT 19
|
#define PROGRAM_VIEW_HEIGHT 19
|
||||||
#define PROGRAM_VIEW_WIDTH 42
|
#define PROGRAM_VIEW_WIDTH 42
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
set(HEADERS gui.h window.h main_window.h)
|
set(HEADERS gui.h main_window.h)
|
||||||
set(SOURCE gui.c window.c main_window.c)
|
set(SOURCE gui.c main_window.c)
|
||||||
|
|
||||||
if (NES_DEBUG)
|
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)
|
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})
|
add_library(nes_gui ${SOURCE} ${HEADERS})
|
||||||
|
|
||||||
find_package(SDL2 REQUIRED)
|
add_subdirectory(components)
|
||||||
include_directories(nes_gui ${SDL2_INCLUDE_DIRS})
|
|
||||||
|
|
||||||
target_link_libraries(nes_gui log.c ${SDL2_LIBRARIES} SDL2_ttf)
|
target_link_libraries(nes_gui nes_gui_components log.c SDL2_ttf)
|
|
@ -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})
|
|
@ -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
|
|
@ -0,0 +1,87 @@
|
||||||
|
//
|
||||||
|
// Created by william on 1/16/24.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <malloc.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#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;
|
||||||
|
}
|
|
@ -0,0 +1,71 @@
|
||||||
|
//
|
||||||
|
// Created by william on 1/16/24.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#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
|
|
@ -0,0 +1,134 @@
|
||||||
|
//
|
||||||
|
// 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
|
||||||
|
}
|
|
@ -0,0 +1,91 @@
|
||||||
|
//
|
||||||
|
// Created by william on 17/05/24.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef NES_EMULATOR_WINDOW_H
|
||||||
|
#define NES_EMULATOR_WINDOW_H
|
||||||
|
|
||||||
|
#include <SDL.h>
|
||||||
|
#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
|
|
@ -0,0 +1,251 @@
|
||||||
|
//
|
||||||
|
// Created by william on 8/23/24.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <SDL_ttf.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#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;
|
||||||
|
}
|
|
@ -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
|
29
gui/gui.c
29
gui/gui.c
|
@ -14,6 +14,12 @@
|
||||||
#include "char_map.h"
|
#include "char_map.h"
|
||||||
#include "dbg_palette.h"
|
#include "dbg_palette.h"
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
#define WINDOW_ID_MAIN 3
|
||||||
|
#else
|
||||||
|
#define WINDOW_ID_MAIN 1
|
||||||
|
#endif
|
||||||
|
|
||||||
typedef struct nes_gui {
|
typedef struct nes_gui {
|
||||||
NesMainWindow main_window;
|
NesMainWindow main_window;
|
||||||
NesPatternWindow pattern_window;
|
NesPatternWindow pattern_window;
|
||||||
|
@ -43,7 +49,7 @@ bool gui_init() {
|
||||||
pattern_window_init(&gui.pattern_window);
|
pattern_window_init(&gui.pattern_window);
|
||||||
nametable_window_init(&gui.nametable_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
|
#endif
|
||||||
|
|
||||||
main_window_init(&gui.main_window);
|
main_window_init(&gui.main_window);
|
||||||
|
@ -70,7 +76,7 @@ void gui_post_sysinit() {
|
||||||
dbg_pattern_table_init();
|
dbg_pattern_table_init();
|
||||||
dbg_nametable_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);
|
pattern_window_build_table(&gui.pattern_window);
|
||||||
nametable_window_update(&gui.nametable_window);
|
nametable_window_update(&gui.nametable_window);
|
||||||
#endif
|
#endif
|
||||||
|
@ -118,6 +124,19 @@ int gui_input() {
|
||||||
#endif
|
#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;
|
return 1;
|
||||||
|
@ -142,8 +161,6 @@ void gui_present() {
|
||||||
pattern_window_present(&gui.pattern_window);
|
pattern_window_present(&gui.pattern_window);
|
||||||
nametable_window_present(&gui.nametable_window);
|
nametable_window_present(&gui.nametable_window);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
main_window_present(&gui.main_window);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void gui_delay() {
|
void gui_delay() {
|
||||||
|
@ -159,6 +176,10 @@ void gui_delay() {
|
||||||
gui.last_frame_tick = SDL_GetTicks();
|
gui.last_frame_tick = SDL_GetTicks();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TTF_Font *gui_get_font() {
|
||||||
|
return gui.font;
|
||||||
|
}
|
||||||
|
|
||||||
unsigned int gui_get_frame_delay() {
|
unsigned int gui_get_frame_delay() {
|
||||||
return gui.frame_delay;
|
return gui.frame_delay;
|
||||||
}
|
}
|
|
@ -6,6 +6,7 @@
|
||||||
#define NES_EMULATOR_GUI_H
|
#define NES_EMULATOR_GUI_H
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
#include <SDL_ttf.h>
|
||||||
|
|
||||||
bool gui_init();
|
bool gui_init();
|
||||||
void gui_uninit();
|
void gui_uninit();
|
||||||
|
@ -17,6 +18,7 @@ void gui_render();
|
||||||
void gui_present();
|
void gui_present();
|
||||||
void gui_delay();
|
void gui_delay();
|
||||||
|
|
||||||
|
TTF_Font* gui_get_font();
|
||||||
unsigned int gui_get_frame_delay();
|
unsigned int gui_get_frame_delay();
|
||||||
|
|
||||||
#endif //NES_EMULATOR_GUI_H
|
#endif //NES_EMULATOR_GUI_H
|
||||||
|
|
|
@ -7,19 +7,37 @@
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "char_map.h"
|
#include "char_map.h"
|
||||||
#include "gui.h"
|
#include "gui.h"
|
||||||
|
#include "components/window_menu.h"
|
||||||
|
|
||||||
void main_window_init(NesMainWindow *window) {
|
void main_window_init(NesMainWindow *window) {
|
||||||
window->sdl_context = window_init("NES Emulator", MAIN_WINDOW_WIDTH, MAIN_WINDOW_HEIGHT, MAIN_WINDOW_SCALE);
|
window->window = window_create("NES Emulator", MAIN_WINDOW_WIDTH, MAIN_WINDOW_HEIGHT, MAIN_WINDOW_SCALE);
|
||||||
window->texture = SDL_CreateTexture(window->sdl_context.renderer, SDL_PIXELFORMAT_ARGB8888,
|
window->texture = window_create_texture(&window->window, SDL_TEXTUREACCESS_STREAMING);
|
||||||
SDL_TEXTUREACCESS_STREAMING, MAIN_WINDOW_WIDTH, MAIN_WINDOW_HEIGHT);
|
|
||||||
|
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) {
|
void main_window_uninit(NesMainWindow *window) {
|
||||||
SDL_DestroyTexture(window->texture);
|
SDL_DestroyTexture(window->texture);
|
||||||
window_uninit(window->sdl_context);
|
window_destroy(&window->window);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
|
|
||||||
void main_window_render_delay(SDL_Renderer *renderer) {
|
void main_window_render_delay(SDL_Renderer *renderer) {
|
||||||
Uint32 delay = gui_get_frame_delay();
|
Uint32 delay = gui_get_frame_delay();
|
||||||
|
|
||||||
|
@ -32,18 +50,22 @@ void main_window_render_delay(SDL_Renderer *renderer) {
|
||||||
|
|
||||||
char_map_render(renderer, buffer);
|
char_map_render(renderer, buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void main_window_render(NesMainWindow *window, pixel *pixels) {
|
void main_window_render(NesMainWindow *window, pixel *pixels) {
|
||||||
SDL_RenderClear(window->sdl_context.renderer);
|
|
||||||
SDL_UpdateTexture(window->texture, NULL, pixels, 256 * sizeof(pixel));
|
SDL_UpdateTexture(window->texture, NULL, pixels, 256 * sizeof(pixel));
|
||||||
SDL_RenderCopy(window->sdl_context.renderer, window->texture, NULL, NULL);
|
|
||||||
|
|
||||||
#if DEBUG
|
window_render_texture(&window->window, window->texture);
|
||||||
main_window_render_delay(window->sdl_context.renderer);
|
window_render_components(&window->window);
|
||||||
#endif
|
|
||||||
|
window_present(&window->window);
|
||||||
}
|
}
|
||||||
|
|
||||||
void main_window_present(NesMainWindow *window) {
|
void main_window_mouse_motion(NesMainWindow *window, int x, int y) {
|
||||||
SDL_RenderPresent(window->sdl_context.renderer);
|
window_mouse_motion(&window->window, x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
void main_window_mouse_click(NesMainWindow *window) {
|
||||||
|
window_mouse_click(&window->window);
|
||||||
}
|
}
|
|
@ -7,14 +7,14 @@
|
||||||
|
|
||||||
#include <SDL.h>
|
#include <SDL.h>
|
||||||
#include "../include/ppu.h"
|
#include "../include/ppu.h"
|
||||||
#include "window.h"
|
#include "components/window.h"
|
||||||
|
|
||||||
#define MAIN_WINDOW_WIDTH 256
|
#define MAIN_WINDOW_WIDTH 256
|
||||||
#define MAIN_WINDOW_HEIGHT 240
|
#define MAIN_WINDOW_HEIGHT 240
|
||||||
#define MAIN_WINDOW_SCALE 3
|
#define MAIN_WINDOW_SCALE 3
|
||||||
|
|
||||||
typedef struct nes_main_window {
|
typedef struct nes_main_window {
|
||||||
NesSdlContext sdl_context;
|
Window window;
|
||||||
|
|
||||||
SDL_Texture *texture;
|
SDL_Texture *texture;
|
||||||
} NesMainWindow;
|
} NesMainWindow;
|
||||||
|
@ -23,10 +23,7 @@ void main_window_init(NesMainWindow *window);
|
||||||
void main_window_uninit(NesMainWindow *window);
|
void main_window_uninit(NesMainWindow *window);
|
||||||
|
|
||||||
void main_window_render(NesMainWindow *window, pixel* pixels);
|
void main_window_render(NesMainWindow *window, pixel* pixels);
|
||||||
void main_window_present(NesMainWindow *window);
|
void main_window_mouse_motion(NesMainWindow *window, int x, int y);
|
||||||
|
void main_window_mouse_click(NesMainWindow *window);
|
||||||
#if DEBUG
|
|
||||||
void main_window_render_delay(SDL_Renderer *renderer);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif //NES_EMULATOR_MAIN_WINDOW_H
|
#endif //NES_EMULATOR_MAIN_WINDOW_H
|
||||||
|
|
|
@ -10,15 +10,13 @@
|
||||||
#define NW_BUFFER_SIZE (NAMETABLE_ROW_WIDTH * NAMETABLE_COL_HEIGHT * PATTERN_DRAW_SIZE * PATTERN_DRAW_SIZE)
|
#define NW_BUFFER_SIZE (NAMETABLE_ROW_WIDTH * NAMETABLE_COL_HEIGHT * PATTERN_DRAW_SIZE * PATTERN_DRAW_SIZE)
|
||||||
|
|
||||||
void nametable_window_init(NesNametableWindow *window) {
|
void nametable_window_init(NesNametableWindow *window) {
|
||||||
window->sdl_context = window_init("Nametable", NW_WIDTH, NW_HEIGHT, NW_SCALE);
|
window->window = window_create("Nametable", NW_WIDTH, NW_HEIGHT, NW_SCALE);
|
||||||
|
window->texture = window_create_texture(&window->window, SDL_TEXTUREACCESS_STREAMING);
|
||||||
window->texture = SDL_CreateTexture(window->sdl_context.renderer, SDL_PIXELFORMAT_ARGB8888,
|
|
||||||
SDL_TEXTUREACCESS_STREAMING, NW_WIDTH, NW_HEIGHT);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void nametable_window_uninit(NesNametableWindow *window) {
|
void nametable_window_uninit(NesNametableWindow *window) {
|
||||||
SDL_DestroyTexture(window->texture);
|
SDL_DestroyTexture(window->texture);
|
||||||
window_uninit(window->sdl_context);
|
window_destroy(&window->window);
|
||||||
}
|
}
|
||||||
|
|
||||||
void nametable_window_update_bank(NesNametableWindow *window, int bank, pixel *buffer) {
|
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) {
|
void nametable_window_render(NesNametableWindow *window) {
|
||||||
SDL_RenderClear(window->sdl_context.renderer);
|
window_render_texture(&window->window, window->texture);
|
||||||
SDL_RenderCopy(window->sdl_context.renderer, window->texture, NULL, NULL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void nametable_window_present(NesNametableWindow *window) {
|
void nametable_window_present(NesNametableWindow *window) {
|
||||||
SDL_RenderPresent(window->sdl_context.renderer);
|
window_present(&window->window);
|
||||||
}
|
}
|
|
@ -5,7 +5,7 @@
|
||||||
#ifndef NES_EMULATOR_NAMETABLE_WINDOW_H
|
#ifndef NES_EMULATOR_NAMETABLE_WINDOW_H
|
||||||
#define NES_EMULATOR_NAMETABLE_WINDOW_H
|
#define NES_EMULATOR_NAMETABLE_WINDOW_H
|
||||||
|
|
||||||
#include "window.h"
|
#include "components/window.h"
|
||||||
#include "../include/types.h"
|
#include "../include/types.h"
|
||||||
|
|
||||||
#define NW_SCALE 1
|
#define NW_SCALE 1
|
||||||
|
@ -13,16 +13,18 @@
|
||||||
#define NW_ROW_TILE_COUNT 64
|
#define NW_ROW_TILE_COUNT 64
|
||||||
|
|
||||||
typedef struct nes_nametable_window {
|
typedef struct nes_nametable_window {
|
||||||
NesSdlContext sdl_context;
|
Window window;
|
||||||
SDL_Texture *texture;
|
SDL_Texture *texture;
|
||||||
} NesNametableWindow;
|
} NesNametableWindow;
|
||||||
|
|
||||||
void nametable_window_init(NesNametableWindow *window);
|
void nametable_window_init(NesNametableWindow *window);
|
||||||
|
|
||||||
void nametable_window_uninit(NesNametableWindow *window);
|
void nametable_window_uninit(NesNametableWindow *window);
|
||||||
|
|
||||||
void nametable_window_update(NesNametableWindow *window);
|
void nametable_window_update(NesNametableWindow *window);
|
||||||
|
|
||||||
void nametable_window_render(NesNametableWindow *window);
|
void nametable_window_render(NesNametableWindow *window);
|
||||||
|
|
||||||
void nametable_window_present(NesNametableWindow *window);
|
void nametable_window_present(NesNametableWindow *window);
|
||||||
|
|
||||||
#endif //NES_EMULATOR_NAMETABLE_WINDOW_H
|
#endif //NES_EMULATOR_NAMETABLE_WINDOW_H
|
||||||
|
|
|
@ -10,16 +10,14 @@
|
||||||
#define PW_BUFFER_SIZE (PW_WIDTH * PW_HEIGHT)
|
#define PW_BUFFER_SIZE (PW_WIDTH * PW_HEIGHT)
|
||||||
|
|
||||||
void pattern_window_init(NesPatternWindow *window) {
|
void pattern_window_init(NesPatternWindow *window) {
|
||||||
window->sdl_context = window_init("Pattern Table", PW_WIDTH, PW_HEIGHT, PW_SCALE);
|
window->window = window_create("Pattern Table", PW_WIDTH, PW_HEIGHT, PW_SCALE);
|
||||||
|
window->texture = window_create_texture(&window->window, SDL_TEXTUREACCESS_STATIC);
|
||||||
window->texture = SDL_CreateTexture(window->sdl_context.renderer, SDL_PIXELFORMAT_ARGB8888,
|
|
||||||
SDL_TEXTUREACCESS_STATIC, PW_WIDTH, PW_HEIGHT);
|
|
||||||
window->palette = 0;
|
window->palette = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void pattern_window_uninit(NesPatternWindow *window) {
|
void pattern_window_uninit(NesPatternWindow *window) {
|
||||||
SDL_DestroyTexture(window->texture);
|
SDL_DestroyTexture(window->texture);
|
||||||
window_uninit(window->sdl_context);
|
window_destroy(&window->window);
|
||||||
}
|
}
|
||||||
|
|
||||||
void pattern_window_build_table(NesPatternWindow *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) {
|
void pattern_window_render(NesPatternWindow *window) {
|
||||||
SDL_RenderClear(window->sdl_context.renderer);
|
window_render_texture(&window->window, window->texture);
|
||||||
SDL_RenderCopy(window->sdl_context.renderer, window->texture, NULL, NULL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void pattern_window_present(NesPatternWindow *window) {
|
void pattern_window_present(NesPatternWindow *window) {
|
||||||
SDL_RenderPresent(window->sdl_context.renderer);
|
window_present(&window->window);
|
||||||
}
|
}
|
|
@ -6,14 +6,14 @@
|
||||||
#define NES_EMULATOR_PATTERN_WINDOW_H
|
#define NES_EMULATOR_PATTERN_WINDOW_H
|
||||||
|
|
||||||
#include "../include/types.h"
|
#include "../include/types.h"
|
||||||
#include "window.h"
|
#include "components/window.h"
|
||||||
|
|
||||||
#define PW_SCALE 2
|
#define PW_SCALE 2
|
||||||
#define PW_ROW_TILE_COUNT 16
|
#define PW_ROW_TILE_COUNT 16
|
||||||
#define PW_PALETTE_MAX 3
|
#define PW_PALETTE_MAX 3
|
||||||
|
|
||||||
typedef struct nes_pattern_window {
|
typedef struct nes_pattern_window {
|
||||||
NesSdlContext sdl_context;
|
Window window;
|
||||||
SDL_Texture *texture;
|
SDL_Texture *texture;
|
||||||
byte palette;
|
byte palette;
|
||||||
} NesPatternWindow;
|
} NesPatternWindow;
|
||||||
|
|
101
gui/window.c
101
gui/window.c
|
@ -1,101 +0,0 @@
|
||||||
//
|
|
||||||
// Created by william on 17/05/24.
|
|
||||||
//
|
|
||||||
|
|
||||||
#include <SDL.h>
|
|
||||||
#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);
|
|
||||||
//}
|
|
18
gui/window.h
18
gui/window.h
|
@ -1,18 +0,0 @@
|
||||||
//
|
|
||||||
// Created by william on 17/05/24.
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef NES_EMULATOR_WINDOW_H
|
|
||||||
#define NES_EMULATOR_WINDOW_H
|
|
||||||
|
|
||||||
#include <SDL.h>
|
|
||||||
|
|
||||||
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
|
|
5
main.c
5
main.c
|
@ -50,7 +50,7 @@ FILE *add_log_file(char *name, int level) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void init_logging() {
|
void init_logging() {
|
||||||
log_set_level(LOG_DEBUG);
|
log_set_level(LOG_INFO);
|
||||||
|
|
||||||
// Print current working directory
|
// Print current working directory
|
||||||
char cwd[256];
|
char cwd[256];
|
||||||
|
@ -81,7 +81,10 @@ void init_logging() {
|
||||||
|
|
||||||
void close_logging() {
|
void close_logging() {
|
||||||
fclose(log_files.info);
|
fclose(log_files.info);
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
fclose(log_files.debug);
|
fclose(log_files.debug);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
set(HEADERS linked_list.h)
|
|
||||||
set(SOURCE linked_list.c)
|
|
||||||
|
|
||||||
add_library(nes_utils ${HEADERS} ${SOURCE})
|
|
|
@ -1,126 +0,0 @@
|
||||||
//
|
|
||||||
// Created by william on 1/16/24.
|
|
||||||
//
|
|
||||||
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <malloc.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <assert.h>
|
|
||||||
#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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,82 +0,0 @@
|
||||||
//
|
|
||||||
// Created by william on 1/16/24.
|
|
||||||
//
|
|
||||||
|
|
||||||
#include <stdbool.h>
|
|
||||||
|
|
||||||
#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
|
|
Loading…
Reference in New Issue