// // Created by william on 16/05/24. // #include #include #include "log.h" #include "gui.h" #include "main_window.h" #include "pattern_window.h" #include "../include/system.h" #include "nametable_window.h" #include "dbg_pattern_table.h" #include "dbg_nametable.h" #include "char_map.h" #include "dbg_palette.h" typedef struct nes_gui { bool window_types_open[WINDOW_TYPE_MAX + 1]; void *window_types_ref[WINDOW_TYPE_MAX + 1]; int window_types_ids[WINDOW_TYPE_MAX + 1]; TTF_Font *font; Uint32 last_frame_tick; Uint32 frame_delay; unsigned long tick; } NesGui; NesGui gui; #define GUI_WINDOW_OPEN_(window_type) gui.window_types_open[window_type] #define GUI_WINDOW_REF_(window_type) gui.window_types_ref[window_type] #define GUI_WINDOW_ACTION_BASE(window_type, call) \ if (GUI_WINDOW_OPEN_(window_type)) { \ call(GUI_WINDOW_REF_(window_type)); \ } void gui_window_create(WindowType type) { void **ref = &gui.window_types_ref[type]; if (gui.window_types_open[type]) { return; } gui.window_types_open[type] = true; int window_id = -1; switch (type) { case WINDOW_TYPE_MAIN: *ref = main_window_create(&window_id); break; case WINDOW_TYPE_NAMETABLE: *ref = nametable_window_create(&window_id); break; case WINDOW_TYPE_PATTERN_TABLE: *ref = pattern_window_create(&window_id); break; } gui.window_types_ids[type] = window_id; } void gui_window_destroy(WindowType type) { bool *open = &gui.window_types_open[type]; if (*open == false) { // The window doesn't exist return; } void *ref = gui.window_types_ref[type]; switch (type) { case WINDOW_TYPE_MAIN: main_window_destroy(ref); break; case WINDOW_TYPE_NAMETABLE: nametable_window_destroy(ref); break; case WINDOW_TYPE_PATTERN_TABLE: pattern_window_destroy(ref); break; } gui.window_types_ids[type] = -1; *open = false; } void gui_window_destroy_by_id(int window_id) { WindowType type = -1; for (int i = 0; i <= WINDOW_TYPE_MAX; i++) { if (gui.window_types_ids[i] == window_id) { type = i; } } if (type == -1) { // Close event is sent twice? log_error("Couldn't find window with ID %d", window_id); return; } gui_window_destroy(type); } bool gui_init() { memset(gui.window_types_open, false, sizeof(bool) * WINDOW_TYPE_MAX); memset(gui.window_types_ref, 0, sizeof(void *) * WINDOW_TYPE_MAX); TTF_Init(); gui.font = TTF_OpenFont("./nintendo-nes-font.ttf", 16); if (gui.font == NULL) { log_error("Failed to open TTF font"); return false; } memset(gui.window_types_ids, -1, (WINDOW_TYPE_MAX + 1) * sizeof(*gui.window_types_ids)); gui_window_create(WINDOW_TYPE_MAIN); return true; } void gui_free() { for (int i = 0; i < WINDOW_TYPE_MAX; i++) { gui_window_destroy(i); } TTF_CloseFont(gui.font); } bool lctrl = false; int gui_input() { assert(gui.window_types_open[WINDOW_TYPE_MAIN]); SDL_Event event; PPUDebugFlags *ppu_debug = &ppu_get_state()->debug; while (SDL_PollEvent(&event)) { if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_CLOSE) { Uint32 window_id = event.window.windowID; for (int type = 0; type <= WINDOW_TYPE_MAX; type++) { bool is_main_window = gui.window_types_ids[WINDOW_TYPE_MAIN] == window_id; gui_window_destroy_by_id(window_id); if (is_main_window) { return -1; } } } if (event.type == SDL_KEYDOWN && event.key.keysym.sym == SDLK_LCTRL) { lctrl = true; } if (event.type == SDL_KEYUP) { switch (event.key.keysym.sym) { case SDLK_LCTRL: lctrl = false; break; case SDLK_p: system_toggle_pause(lctrl); break; 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: if (gui.window_types_open[WINDOW_TYPE_PATTERN_TABLE]) { pattern_window_key_up(gui.window_types_ref[WINDOW_TYPE_PATTERN_TABLE], event.key.keysym.sym); } break; } } void *main_window_ref = gui.window_types_ref[WINDOW_TYPE_MAIN]; if (event.type == SDL_MOUSEMOTION) { int x = event.motion.x; int y = event.motion.y; if (gui.window_types_ids[WINDOW_TYPE_MAIN] == event.window.windowID) { main_window_mouse_motion(main_window_ref, x, y); } } if (event.type == SDL_MOUSEBUTTONUP && gui.window_types_ids[WINDOW_TYPE_MAIN] == event.window.windowID) { main_window_mouse_click(main_window_ref); } } return 1; } #define GUI_WINDOW_RENDER(window_type, prefix) GUI_WINDOW_ACTION_BASE(window_type, prefix ## _window_render) void gui_render() { assert(gui.window_types_open[WINDOW_TYPE_MAIN]); main_window_render(gui.window_types_ref[WINDOW_TYPE_MAIN], ppu_get_state()->pixels); // Update the nametable GUI_WINDOW_ACTION_BASE(WINDOW_TYPE_NAMETABLE, nametable_window_update); GUI_WINDOW_RENDER(WINDOW_TYPE_NAMETABLE, nametable); // Update the pattern table GUI_WINDOW_ACTION_BASE(WINDOW_TYPE_PATTERN_TABLE, pattern_window_update); GUI_WINDOW_RENDER(WINDOW_TYPE_PATTERN_TABLE, pattern) gui.tick++; } void gui_delay() { Uint32 tick_now = SDL_GetTicks(); gui.frame_delay = tick_now - gui.last_frame_tick; if (gui.frame_delay < 16) { Uint32 additional_delay = 16 - gui.frame_delay; SDL_Delay(additional_delay); gui.frame_delay += additional_delay; } gui.last_frame_tick = SDL_GetTicks(); } TTF_Font *gui_get_font() { return gui.font; }