application.cpp (15860B)
1/* 2 * Gearboy - Nintendo Game Boy Emulator 3 * Copyright (C) 2012 Ignacio Sanchez 4 5 * This program is free software: you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation, either version 3 of the License, or 8 * any later version. 9 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see http://www.gnu.org/licenses/ 17 * 18 */ 19 20#include <SDL.h> 21#include "imgui/imgui.h" 22#include "imgui/imgui_impl_sdl.h" 23#include "emu.h" 24#include "gui.h" 25#include "config.h" 26#include "renderer.h" 27 28#define APPLICATION_IMPORT 29#include "application.h" 30 31static SDL_Window* sdl_window; 32static SDL_GLContext gl_context; 33static bool running = true; 34static bool paused_when_focus_lost = false; 35static Uint64 frame_time_start; 36static Uint64 frame_time_end; 37 38static int sdl_init(void); 39static void sdl_destroy(void); 40static void sdl_events(void); 41static void sdl_events_emu(const SDL_Event* event); 42static void sdl_shortcuts_gui(const SDL_Event* event); 43static void run_emulator(void); 44static void render(void); 45static void frame_throttle(void); 46 47int application_init(const char* arg) 48{ 49 Log ("<·> %s %s Desktop App <·>", GEARBOY_TITLE, GEARBOY_VERSION); 50 51 if (IsValidPointer(arg) && (strlen(arg) > 0)) 52 { 53 Log ("Loading with argv: %s"); 54 } 55 56 int ret = sdl_init(); 57 58 application_fullscreen = false; 59 60 config_init(); 61 config_read(); 62 63 emu_init(); 64 65 strcpy(emu_savefiles_path, config_emulator.savefiles_path.c_str()); 66 strcpy(emu_savestates_path, config_emulator.savestates_path.c_str()); 67 emu_savefiles_dir_option = config_emulator.savefiles_dir_option; 68 emu_savestates_dir_option = config_emulator.savestates_dir_option; 69 70 gui_init(); 71 72 ImGui_ImplSDL2_InitForOpenGL(sdl_window, gl_context); 73 74 renderer_init(); 75 76 SDL_GL_SetSwapInterval(config_video.sync ? 1 : 0); 77 78 if (IsValidPointer(arg) && (strlen(arg) > 0)) 79 { 80 gui_load_rom(arg); 81 } 82 83 return ret; 84} 85 86void application_destroy(void) 87{ 88 config_write(); 89 config_destroy(); 90 renderer_destroy(); 91 gui_destroy(); 92 emu_destroy(); 93 sdl_destroy(); 94} 95 96void application_mainloop(void) 97{ 98 while (running) 99 { 100 frame_time_start = SDL_GetPerformanceCounter(); 101 sdl_events(); 102 run_emulator(); 103 render(); 104 frame_time_end = SDL_GetPerformanceCounter(); 105 frame_throttle(); 106 } 107} 108 109void application_trigger_quit(void) 110{ 111 SDL_Event event; 112 event.type = SDL_QUIT; 113 SDL_PushEvent(&event); 114} 115 116void application_trigger_fullscreen(bool fullscreen) 117{ 118 SDL_SetWindowFullscreen(sdl_window, fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0); 119} 120 121static int sdl_init(void) 122{ 123 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_GAMECONTROLLER) != 0) 124 { 125 Log("Error: %s\n", SDL_GetError()); 126 return -1; 127 } 128 129 SDL_VERSION(&application_sdl_build_version); 130 SDL_GetVersion(&application_sdl_link_version); 131 132 SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); 133 SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); 134 SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8); 135 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); 136 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2); 137 SDL_WindowFlags window_flags = (SDL_WindowFlags)(SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI); 138 sdl_window = SDL_CreateWindow(GEARBOY_TITLE " " GEARBOY_VERSION, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 800, 700, window_flags); 139 gl_context = SDL_GL_CreateContext(sdl_window); 140 SDL_GL_MakeCurrent(sdl_window, gl_context); 141 SDL_GL_SetSwapInterval(0); 142 143 SDL_SetWindowMinimumSize(sdl_window, 644, 602); 144 145 application_gamepad_mappings = SDL_GameControllerAddMappingsFromRW(SDL_RWFromFile("gamecontrollerdb.txt", "rb"), 1); 146 147 if (application_gamepad_mappings > 0) 148 { 149 Log("Succesfuly loaded %d game controller mappings", application_gamepad_mappings); 150 } 151 else 152 { 153 Log("Game controller database not found!"); 154 } 155 156 for (int i = 0; i < SDL_NumJoysticks(); ++i) 157 { 158 if (SDL_IsGameController(i)) 159 { 160 application_gamepad = SDL_GameControllerOpen(i); 161 if(!application_gamepad) 162 { 163 Log("Warning: Unable to open game controller! SDL Error: %s\n", SDL_GetError()); 164 } 165 else 166 { 167 Log("Game controller %d correctly detected", i); 168 } 169 170 break; 171 } 172 } 173 174 int w, h; 175 int display_w, display_h; 176 SDL_GetWindowSize(sdl_window, &w, &h); 177 SDL_GL_GetDrawableSize(sdl_window, &display_w, &display_h); 178 179 if (w > 0 && h > 0) 180 { 181 float scale_w = (float)display_w / w; 182 float scale_h = (float)display_h / h; 183 184 application_display_scale = (scale_w > scale_h) ? scale_w : scale_h; 185 } 186 187 SDL_EventState(SDL_DROPFILE, SDL_ENABLE); 188 189 return 0; 190} 191 192static void sdl_destroy(void) 193{ 194 SDL_GameControllerClose(application_gamepad); 195 ImGui_ImplSDL2_Shutdown(); 196 SDL_GL_DeleteContext(gl_context); 197 SDL_DestroyWindow(sdl_window); 198 SDL_Quit(); 199} 200 201static void sdl_events(void) 202{ 203 SDL_Event event; 204 205 while (SDL_PollEvent(&event)) 206 { 207 if (event.type == SDL_QUIT) 208 { 209 running = false; 210 break; 211 } 212 213 ImGui_ImplSDL2_ProcessEvent(&event); 214 215 if (!gui_in_use) 216 { 217 sdl_events_emu(&event); 218 sdl_shortcuts_gui(&event); 219 } 220 } 221} 222 223static void sdl_events_emu(const SDL_Event* event) 224{ 225 switch(event->type) 226 { 227 case (SDL_DROPFILE): 228 { 229 char* dropped_filedir = event->drop.file; 230 gui_load_rom(dropped_filedir); 231 SDL_free(dropped_filedir); // Free dropped_filedir memory 232 break; 233 } 234 case SDL_WINDOWEVENT: 235 { 236 switch (event->window.event) 237 { 238 case SDL_WINDOWEVENT_FOCUS_GAINED: 239 { 240 if (!paused_when_focus_lost) 241 emu_resume(); 242 } 243 break; 244 245 case SDL_WINDOWEVENT_FOCUS_LOST: 246 { 247 paused_when_focus_lost = emu_is_paused(); 248 emu_pause(); 249 } 250 break; 251 } 252 } 253 break; 254 255 case SDL_CONTROLLERBUTTONDOWN: 256 { 257 if (!config_input.gamepad) 258 break; 259 260 if (event->cbutton.button == config_input.gamepad_b) 261 emu_key_pressed(B_Key); 262 else if (event->cbutton.button == config_input.gamepad_a) 263 emu_key_pressed(A_Key); 264 else if (event->cbutton.button == config_input.gamepad_select) 265 emu_key_pressed(Select_Key); 266 else if (event->cbutton.button == config_input.gamepad_start) 267 emu_key_pressed(Start_Key); 268 269 if (config_input.gamepad_directional == 1) 270 break; 271 272 if (event->cbutton.button == SDL_CONTROLLER_BUTTON_DPAD_UP) 273 emu_key_pressed(Up_Key); 274 else if (event->cbutton.button == SDL_CONTROLLER_BUTTON_DPAD_DOWN) 275 emu_key_pressed(Down_Key); 276 else if (event->cbutton.button == SDL_CONTROLLER_BUTTON_DPAD_LEFT) 277 emu_key_pressed(Left_Key); 278 else if (event->cbutton.button == SDL_CONTROLLER_BUTTON_DPAD_RIGHT) 279 emu_key_pressed(Right_Key); 280 } 281 break; 282 283 case SDL_CONTROLLERBUTTONUP: 284 { 285 if (!config_input.gamepad) 286 break; 287 288 if (event->cbutton.button == config_input.gamepad_b) 289 emu_key_released(B_Key); 290 else if (event->cbutton.button == config_input.gamepad_a) 291 emu_key_released(A_Key); 292 else if (event->cbutton.button == config_input.gamepad_select) 293 emu_key_released(Select_Key); 294 else if (event->cbutton.button == config_input.gamepad_start) 295 emu_key_released(Start_Key); 296 297 if (config_input.gamepad_directional == 1) 298 break; 299 300 if (event->cbutton.button == SDL_CONTROLLER_BUTTON_DPAD_UP) 301 emu_key_released(Up_Key); 302 else if (event->cbutton.button == SDL_CONTROLLER_BUTTON_DPAD_DOWN) 303 emu_key_released(Down_Key); 304 else if (event->cbutton.button == SDL_CONTROLLER_BUTTON_DPAD_LEFT) 305 emu_key_released(Left_Key); 306 else if (event->cbutton.button == SDL_CONTROLLER_BUTTON_DPAD_RIGHT) 307 emu_key_released(Right_Key); 308 } 309 break; 310 311 case SDL_CONTROLLERAXISMOTION: 312 { 313 if (!config_input.gamepad) 314 break; 315 316 if (config_input.gamepad_directional == 0) 317 break; 318 319 const int STICK_DEAD_ZONE = 8000; 320 321 if(event->caxis.axis == config_input.gamepad_x_axis) 322 { 323 int x_motion = event->caxis.value * (config_input.gamepad_invert_x_axis ? -1 : 1); 324 325 if (x_motion < -STICK_DEAD_ZONE) 326 emu_key_pressed(Left_Key); 327 else if (x_motion > STICK_DEAD_ZONE) 328 emu_key_pressed(Right_Key); 329 else 330 { 331 emu_key_released(Left_Key); 332 emu_key_released(Right_Key); 333 } 334 } 335 else if(event->caxis.axis == config_input.gamepad_y_axis) 336 { 337 int y_motion = event->caxis.value * (config_input.gamepad_invert_y_axis ? -1 : 1); 338 339 if (y_motion < -STICK_DEAD_ZONE) 340 emu_key_pressed(Up_Key); 341 else if (y_motion > STICK_DEAD_ZONE) 342 emu_key_pressed(Down_Key); 343 else 344 { 345 emu_key_released(Up_Key); 346 emu_key_released(Down_Key); 347 } 348 } 349 } 350 break; 351 352 case SDL_KEYDOWN: 353 { 354 if (event->key.keysym.mod & KMOD_CTRL) 355 break; 356 357 int key = event->key.keysym.scancode; 358 359 if (key == SDL_SCANCODE_ESCAPE) 360 { 361 application_trigger_quit(); 362 break; 363 } 364 365 if (key == SDL_SCANCODE_F11) 366 { 367 application_fullscreen = !application_fullscreen; 368 application_trigger_fullscreen(application_fullscreen); 369 break; 370 } 371 372 if (key == config_input.key_left) 373 emu_key_pressed(Left_Key); 374 else if (key == config_input.key_right) 375 emu_key_pressed(Right_Key); 376 else if (key == config_input.key_up) 377 emu_key_pressed(Up_Key); 378 else if (key == config_input.key_down) 379 emu_key_pressed(Down_Key); 380 else if (key == config_input.key_b) 381 emu_key_pressed(B_Key); 382 else if (key == config_input.key_a) 383 emu_key_pressed(A_Key); 384 else if (key == config_input.key_select) 385 emu_key_pressed(Select_Key); 386 else if (key == config_input.key_start) 387 emu_key_pressed(Start_Key); 388 } 389 break; 390 391 case SDL_KEYUP: 392 { 393 int key = event->key.keysym.scancode; 394 395 if (key == config_input.key_left) 396 emu_key_released(Left_Key); 397 else if (key == config_input.key_right) 398 emu_key_released(Right_Key); 399 else if (key == config_input.key_up) 400 emu_key_released(Up_Key); 401 else if (key == config_input.key_down) 402 emu_key_released(Down_Key); 403 else if (key == config_input.key_b) 404 emu_key_released(B_Key); 405 else if (key == config_input.key_a) 406 emu_key_released(A_Key); 407 else if (key == config_input.key_select) 408 emu_key_released(Select_Key); 409 else if (key == config_input.key_start) 410 emu_key_released(Start_Key); 411 } 412 break; 413 } 414} 415 416static void sdl_shortcuts_gui(const SDL_Event* event) 417{ 418 if ((event->type == SDL_KEYDOWN) && (event->key.keysym.mod & KMOD_CTRL)) 419 { 420 int key = event->key.keysym.scancode; 421 422 switch (key) 423 { 424 case SDL_SCANCODE_O: 425 gui_shortcut(gui_ShortcutOpenROM); 426 break; 427 case SDL_SCANCODE_R: 428 gui_shortcut(gui_ShortcutReset); 429 break; 430 case SDL_SCANCODE_P: 431 gui_shortcut(gui_ShortcutPause); 432 break; 433 case SDL_SCANCODE_F: 434 gui_shortcut(gui_ShortcutFFWD); 435 break; 436 case SDL_SCANCODE_L: 437 gui_shortcut(gui_ShortcutLoadState); 438 break; 439 case SDL_SCANCODE_S: 440 gui_shortcut(gui_ShortcutSaveState); 441 break; 442 case SDL_SCANCODE_M: 443 gui_shortcut(gui_ShortcutShowMainMenu); 444 break; 445 case SDL_SCANCODE_F5: 446 gui_shortcut(gui_ShortcutDebugContinue); 447 break; 448 case SDL_SCANCODE_F6: 449 gui_shortcut(gui_ShortcutDebugNextFrame); 450 break; 451 case SDL_SCANCODE_F8: 452 gui_shortcut(gui_ShortcutDebugRuntocursor); 453 break; 454 case SDL_SCANCODE_F9: 455 gui_shortcut(gui_ShortcutDebugBreakpoint); 456 break; 457 case SDL_SCANCODE_F10: 458 gui_shortcut(gui_ShortcutDebugStep); 459 break; 460 case SDL_SCANCODE_BACKSPACE: 461 gui_shortcut(gui_ShortcutDebugGoBack); 462 break; 463 } 464 } 465} 466 467static void run_emulator(void) 468{ 469 if (!emu_is_empty()) 470 { 471 static int i = 0; 472 i++; 473 474 if (i > 20) 475 { 476 i = 0; 477 478 char title[256]; 479 sprintf(title, "%s %s - %s", GEARBOY_TITLE, GEARBOY_VERSION, emu_get_core()->GetCartridge()->GetFileName()); 480 SDL_SetWindowTitle(sdl_window, title); 481 } 482 } 483 config_emulator.paused = emu_is_paused(); 484 emu_audio_sync = config_audio.sync; 485 emu_update(); 486} 487 488static void render(void) 489{ 490 renderer_begin_render(); 491 ImGui_ImplSDL2_NewFrame(sdl_window); 492 gui_render(); 493 renderer_render(); 494 renderer_end_render(); 495 496 SDL_GL_SwapWindow(sdl_window); 497} 498 499static void frame_throttle(void) 500{ 501 if (emu_is_empty() || emu_is_paused() || config_emulator.ffwd) 502 { 503 float elapsed = (float)((frame_time_end - frame_time_start) * 1000) / SDL_GetPerformanceFrequency(); 504 505 float min = 16.666f; 506 507 if (config_emulator.ffwd) 508 { 509 switch (config_emulator.ffwd_speed) 510 { 511 case 0: 512 min = 16.666f / 1.5f; 513 break; 514 case 1: 515 min = 16.666f / 2.0f; 516 break; 517 case 2: 518 min = 16.666f / 2.5f; 519 break; 520 case 3: 521 min = 16.666f / 3.0f; 522 break; 523 default: 524 min = 0.0f; 525 } 526 } 527 528 if (elapsed < min) 529 SDL_Delay((Uint32)(min - elapsed)); 530 } 531}