diff --git a/src/input.h b/src/input.h index 27c825c..eeb8ad4 100644 --- a/src/input.h +++ b/src/input.h @@ -18,6 +18,7 @@ typedef enum { input_Result_Cancel, input_Result_View, input_Result_Refresh, + input_Result_Scale, } nes_Input_Result; #define nes_controller_num_buttons (8U) diff --git a/src/menu.c b/src/menu.c index ee63159..d528d32 100644 --- a/src/menu.c +++ b/src/menu.c @@ -268,19 +268,22 @@ char* run_main_menu(menu_state* state, nese_Components* comp, } int run_game_menu(menu_state* state, nese_Components* comp, - const cart_info* cart) { - static char* items[] = { + const nese_State* nese) { + char* items[] = { "Resume", "Save", "Restore", "Reset", "Select ROM", #ifndef STANDALONE - "Toggle Fullscreen", + (nese->flags & (1 << State_Bit_Fullscreen)) ? + "Windowed" : "Fullscreen", #endif + (nese->flags & (1 << State_Bit_Integer_Scale)) ? + "Force Aspect" : "Integer Scaling", "Exit", }; - static int choices[] = { + static const int choices[] = { input_Result_OK, input_Result_Save, input_Result_Load, @@ -289,9 +292,10 @@ int run_game_menu(menu_state* state, nese_Components* comp, #ifndef STANDALONE input_Result_View, #endif + input_Result_Scale, input_Result_Quit, }; - static const file_list options = { + const file_list options = { .files = items, .count = (sizeof(items) / sizeof(*items)), }; @@ -299,7 +303,8 @@ int run_game_menu(menu_state* state, nese_Components* comp, menu_state menu = {0}; if (NULL != state) menu = *state; - int status = run_menu(&menu, &options, 100, comp, cart); + int status = run_menu(&menu, &options, 100, + comp, &nese->cart); if (input_Result_Menu == status) { status = input_Result_Cancel; diff --git a/src/menu.h b/src/menu.h index 533f760..ef0a372 100644 --- a/src/menu.h +++ b/src/menu.h @@ -35,7 +35,7 @@ char* run_main_menu(menu_state*, nese_Components*, // Returns nes_Input_Result indicating the choice int run_game_menu(menu_state*, nese_Components*, - const cart_info*); + const nese_State*); int modal_popup(const char* message, nese_Components*, const cart_info*); diff --git a/src/nese.c b/src/nese.c index 84b7654..312ba37 100644 --- a/src/nese.c +++ b/src/nese.c @@ -169,18 +169,19 @@ static int do_game_menu(menu_state* menu, menu_state rom_menu = {0}; while (1) { - status = run_game_menu(menu, comp, &state->cart); + status = run_game_menu(menu, comp, state); - if (input_Result_View == status) { + if ( input_Result_View == status || + input_Result_Scale == status ) { + if (input_Result_View == status) { #ifndef STANDALONE - state->flags ^= (1 << State_Bit_Fullscreen); + + state->flags ^= (1 << State_Bit_Fullscreen); #endif - // We call this both times since it does both - // the toggle and the recalculation. - nes_render_fullscreen( - comp->rend, - state->flags & (1 << State_Bit_Fullscreen) - ); + } else { + state->flags ^= (1 << State_Bit_Integer_Scale); + } + nes_render_set_flags(comp->rend, state->flags); continue; } else if (input_Result_Menu == status) { @@ -223,11 +224,7 @@ int main(int argc, char* argv[]) { if (status == 0) { status = nes_render_init(components.rend); if (0 == status) { - nes_render_fullscreen( - components.rend, - (state.flags & (1 << State_Bit_Fullscreen)) - ); - nes_render_refresh(components.rend); + nes_render_set_flags( components.rend, state.flags); } } diff --git a/src/render.h b/src/render.h index 6c04a1a..2625a84 100644 --- a/src/render.h +++ b/src/render.h @@ -10,7 +10,7 @@ typedef struct nes_Renderer_t { void (*done)(struct nes_Renderer_t*); int (*render)(struct nes_Renderer_t*, nes_ppu*); - void (*fullscreen)(struct nes_Renderer_t*, int enable); + void (*set_flags)(struct nes_Renderer_t*, uint32_t flags); void (*refresh)(struct nes_Renderer_t*); void (*draw_last_frame)(struct nes_Renderer_t*, int dim); @@ -19,6 +19,7 @@ typedef struct nes_Renderer_t { void (*draw_done)(struct nes_Renderer_t*); Overlay overlay; + uint32_t flags; void* data; } nes_Renderer; @@ -34,9 +35,13 @@ static inline int nes_render(nes_Renderer* rend, nes_ppu* ppu) { return rend->render(rend, ppu); } -static inline void nes_render_fullscreen(nes_Renderer* rend, - int enable) { - rend->fullscreen(rend, enable); +static inline uint32_t nes_render_set_flags(nes_Renderer* rend, + uint32_t flags) { + + uint32_t old_flags = rend->flags; + rend->set_flags(rend, flags); + rend->flags = flags; + return old_flags; } static inline void nes_render_refresh(nes_Renderer* rend) { diff --git a/src/sdl_input.c b/src/sdl_input.c index 85ac971..35701ea 100644 --- a/src/sdl_input.c +++ b/src/sdl_input.c @@ -152,14 +152,7 @@ static int sdl_input_update(nes_Input_Reader* reader, } else if (SDL_WINDOWEVENT == event.type) { if ( SDL_WINDOWEVENT_EXPOSED == event.window.event) { - // We need to do this to flush both buffers -// nes_draw_last_frame(comp->rend, 1); -// nes_draw_done(comp->rend); - - // We call this both times since it does both - // the toggle and the recalculation. nes_render_refresh(comp->rend); - status = input_Result_Refresh; } diff --git a/src/sdl_render.c b/src/sdl_render.c index 02d749a..9eff97a 100644 --- a/src/sdl_render.c +++ b/src/sdl_render.c @@ -37,7 +37,6 @@ static inline uint8_t* chr_mem(const nes_ppu* ppu, return ppu->mapper->chr_addr(ppu->map_data, addr); } - typedef struct { SDL_Window* window; SDL_Renderer* renderer; @@ -47,17 +46,84 @@ typedef struct { sdl_overlay_font font; SDL_Texture* texture; - int win_w; - int win_h; SDL_Rect view; } sdl_render_data; static sdl_render_data the_render_data = {0}; +static void sdl_render_refresh(nes_Renderer* rend) { + sdl_render_data* data = (sdl_render_data*)rend->data; + int win_w = 0, win_h = 0; + SDL_GetWindowSize(data->window, &win_w, &win_h); + + // Determine the viewport within the screen + + int w = win_w; + int h = win_h; + + if (!(rend->flags & (1 << State_Bit_Integer_Scale))) { + // Fullscreen + if ((w * nes_ppu_scan_h) > (h * nes_ppu_scan_w)) { + w = (h * nes_ppu_scan_w) / nes_ppu_scan_h; + } else { + h = (w * nes_ppu_scan_h) / nes_ppu_scan_w; + } + + } else { + // Integer Scale + int scale_y = 1, scale_x = 1; + if ((w * nes_ppu_scan_h) > (h * nes_ppu_scan_w)) { + // Wide Window +// printf("Wide\n"); + scale_y = h / nes_ppu_scan_h; + scale_x = ( (scale_y * nes_ppu_scan_w) + + (nes_ppu_render_w / 2) ) / + nes_ppu_render_w; + if ((scale_x * nes_ppu_render_w) > w) { + scale_x = w / nes_ppu_render_w; + } + + } else { + // Tall Window +// printf("Tall\n"); + scale_x = w / nes_ppu_render_w; + scale_y = ( (scale_x * nes_ppu_render_w) + + (nes_ppu_scan_w / 2) ) / nes_ppu_scan_w; + if ((scale_y * nes_ppu_render_h) > h) { + scale_y = h / nes_ppu_render_h; + } + } + + h = scale_y * nes_ppu_render_h; + w = scale_x * nes_ppu_render_w; +/* + printf("Scale %d, %d : %d, %d -> %d, %d\n", + scale_x, scale_y, win_w, win_h, w, h); +*/ + if (!(rend->flags & (1 << State_Bit_Fullscreen))) { + SDL_SetWindowSize(data->window, w, h); + int x = 0, y = 0; + SDL_GetWindowPosition(data->window, &x, &y); + SDL_SetWindowPosition(data->window, + x - ((w - win_w) / 2), + y - ((h - win_h) / 2)); + } + } + + data->view.x = (win_w - w) / 2; + data->view.y = (win_h - h) / 2; + data->view.w = w; + data->view.h = h; + + SDL_RenderSetClipRect(data->renderer, &data->view); +} + static int sdl_render_init(nes_Renderer* rend) { sdl_render_data* data = &the_render_data; + rend->data = &the_render_data; + int status = SDL_Init(SDL_INIT_VIDEO); if (0 != status) { @@ -70,21 +136,11 @@ static int sdl_render_init(nes_Renderer* rend) { SDL_GetCurrentDisplayMode(0, &mode); - int yscale = (mode.h - 1) / nes_ppu_scan_h; - int xscale = mode.w / nes_ppu_scan_w; - int scale = (xscale < yscale ? xscale : yscale); - - data->win_w = (nes_ppu_scan_w * scale); - data->win_h = (nes_ppu_scan_h * scale); - - data->view.w = data->win_w; - data->view.h = data->win_h; - data->window = SDL_CreateWindow( "NESe", - SDL_WINDOWPOS_UNDEFINED, - SDL_WINDOWPOS_UNDEFINED, - data->win_w, data->win_h, 0 + SDL_WINDOWPOS_CENTERED, + SDL_WINDOWPOS_CENTERED, + mode.w, mode.h, 0 ); if (NULL == data->window) { fprintf(stderr, "SDL: Failed to create window\n"); @@ -106,6 +162,7 @@ static int sdl_render_init(nes_Renderer* rend) { status = -1; } else { + sdl_render_refresh(rend); overlay_init(&rend->overlay); sdl_overlay_font_init(data->renderer, &data->font); } @@ -171,8 +228,6 @@ static int sdl_render_init(nes_Renderer* rend) { SDL_SetPaletteColors(data->foreground->format->palette, nes_palette, 0U, 64U); SDL_SetColorKey(data->foreground, SDL_TRUE, 0xFFU); - - rend->data = &the_render_data; } return status; @@ -190,38 +245,16 @@ static void sdl_render_done(nes_Renderer* rend) { SDL_Quit(); } -static void sdl_render_fullscreen(nes_Renderer* rend, int on) { - sdl_render_data* data = (sdl_render_data*)rend->data; - SDL_SetWindowFullscreen(data->window, - on ? SDL_WINDOW_FULLSCREEN : 0); -} - -static void sdl_render_refresh(nes_Renderer* rend) { +static void sdl_render_set_flags(nes_Renderer* rend, + uint32_t flags) { sdl_render_data* data = (sdl_render_data*)rend->data; - SDL_GetWindowSize(data->window, - &data->win_w, &data->win_h); - - // Determine the viewport within the screen - - int w = data->win_w; - int h = data->win_h; - - if ((w * nes_ppu_scan_h) > (h * nes_ppu_scan_w)) { - w = (h * nes_ppu_scan_w) / nes_ppu_scan_h; - } else { - h = (w * nes_ppu_scan_h) / nes_ppu_scan_w; - } - - data->view.x = (data->win_w - w) / 2; - data->view.y = (data->win_h - h) / 2; - data->view.w = w; - data->view.h = h; - - SDL_RenderSetClipRect(data->renderer, &data->view); - - // We need to do this to flush both buffers -// nes_draw_last_frame(rend, 1); -// nes_draw_done(rend); + SDL_SetWindowFullscreen( + data->window, + (flags & (1 << State_Bit_Fullscreen)) ? + SDL_WINDOW_FULLSCREEN : 0 + ); + rend->flags = flags; + sdl_render_refresh(rend); } static inline void render_sprite_line( @@ -661,7 +694,7 @@ nes_Renderer sdl_renderer = { .init = sdl_render_init, .done = sdl_render_done, .render = sdl_render, - .fullscreen = sdl_render_fullscreen, + .set_flags = sdl_render_set_flags, .refresh = sdl_render_refresh, .draw_last_frame = sdl_redraw_frame, .draw_text = sdl_draw_text, diff --git a/src/state.c b/src/state.c index 0182edc..33d3f82 100644 --- a/src/state.c +++ b/src/state.c @@ -43,12 +43,16 @@ static const ini_datum prefs_schema = { .type = ini_section, .name = ".flags", .offset = offsetof(nese_State, flags), - .count = 1, + .count = 2, .data = (ini_datum[]){ { .type = ini_flag, .name = "fullscreen", .shift = State_Bit_Fullscreen, + }, { + .type = ini_flag, + .name = "integer_scale", + .shift = State_Bit_Integer_Scale, }, } }, diff --git a/src/state.h b/src/state.h index 20a924d..7b8c91f 100644 --- a/src/state.h +++ b/src/state.h @@ -34,11 +34,12 @@ void cart_info_done(cart_info*); typedef enum { State_Bit_Fullscreen = 0, -} nese_State_Flags; + State_Bit_Integer_Scale = 1, +} nese_State_Flag_Bits; typedef struct { cart_info cart; - nese_State_Flags flags; + uint32_t flags; // nese_State_Flag_Bits } nese_State;