|
|
1.1 ! root 1: /* ! 2: Hatari - main.c ! 3: ! 4: This file is distributed under the GNU Public License, version 2 or at ! 5: your option any later version. Read the file gpl.txt for details. ! 6: ! 7: Main initialization and event handling routines. ! 8: */ ! 9: const char Main_fileid[] = "Hatari main.c : " __DATE__ " " __TIME__; ! 10: ! 11: #include <time.h> ! 12: #include <SDL.h> ! 13: ! 14: #include "main.h" ! 15: #include "configuration.h" ! 16: #include "control.h" ! 17: #include "options.h" ! 18: #include "dialog.h" ! 19: #include "ioMem.h" ! 20: #include "log.h" ! 21: #include "m68000.h" ! 22: #include "memorySnapShot.h" ! 23: #include "paths.h" ! 24: #include "reset.h" ! 25: #include "screen.h" ! 26: #include "sdlgui.h" ! 27: #include "shortcut.h" ! 28: #include "statusbar.h" ! 29: #include "nextMemory.h" ! 30: #include "str.h" ! 31: #include "video.h" ! 32: #include "avi_record.h" ! 33: #include "debugui.h" ! 34: #include "keymap.h" ! 35: #include "hatari-glue.h" ! 36: ! 37: ! 38: ! 39: bool bQuitProgram = false; /* Flag to quit program cleanly */ ! 40: ! 41: Uint32 nRunVBLs; /* Whether and how many VBLS to run before exit */ ! 42: static Uint32 nFirstMilliTick; /* Ticks when VBL counting started */ ! 43: static Uint32 nVBLCount; /* Frame count */ ! 44: ! 45: static bool bEmulationActive = true; /* Run emulation when started */ ! 46: static bool bAccurateDelays; /* Host system has an accurate SDL_Delay()? */ ! 47: static bool bIgnoreNextMouseMotion = false; /* Next mouse motion will be ignored (needed after SDL_WarpMouse) */ ! 48: ! 49: ! 50: /*-----------------------------------------------------------------------*/ ! 51: /** ! 52: * Return current time as millisecond for performance measurements. ! 53: * ! 54: * (On Unix only time spent by Hatari itself is counted, on other ! 55: * platforms less accurate SDL "wall clock".) ! 56: */ ! 57: #if HAVE_SYS_TIMES_H ! 58: #include <unistd.h> ! 59: #include <sys/times.h> ! 60: static Uint32 Main_GetTicks(void) ! 61: { ! 62: static unsigned int ticks_to_msec = 0; ! 63: struct tms fields; ! 64: if (!ticks_to_msec) ! 65: { ! 66: ticks_to_msec = sysconf(_SC_CLK_TCK); ! 67: printf("OS clock ticks / second: %d\n", ticks_to_msec); ! 68: /* Linux has 100Hz virtual clock so no accuracy loss there */ ! 69: ticks_to_msec = 1000UL / ticks_to_msec; ! 70: } ! 71: /* return milliseconds (clock ticks) spent in this process ! 72: */ ! 73: times(&fields); ! 74: return ticks_to_msec * fields.tms_utime; ! 75: } ! 76: #else ! 77: # warning "times() function missing, using inaccurate SDL_GetTicks() instead." ! 78: # define Main_GetTicks SDL_GetTicks ! 79: #endif ! 80: ! 81: ! 82: /*-----------------------------------------------------------------------*/ ! 83: /** ! 84: * Pause emulation, stop sound. 'visualize' should be set true, ! 85: * unless unpause will be called immediately afterwards. ! 86: * ! 87: * @return true if paused now, false if was already paused ! 88: */ ! 89: bool Main_PauseEmulation(bool visualize) ! 90: { ! 91: if ( !bEmulationActive ) ! 92: return false; ! 93: ! 94: bEmulationActive = false; ! 95: if (visualize) ! 96: { ! 97: if (nFirstMilliTick) ! 98: { ! 99: int interval = Main_GetTicks() - nFirstMilliTick; ! 100: static float previous; ! 101: float current; ! 102: ! 103: current = (1000.0 * nVBLCount) / interval; ! 104: printf("SPEED: %.1f VBL/s (%d/%.1fs), diff=%.1f%%\n", ! 105: current, nVBLCount, interval/1000.0, ! 106: previous ? 100*(current-previous)/previous : 0.0); ! 107: nVBLCount = nFirstMilliTick = 0; ! 108: previous = current; ! 109: } ! 110: ! 111: Statusbar_AddMessage("Emulation paused", 100); ! 112: /* make sure msg gets shown */ ! 113: Statusbar_Update(sdlscrn); ! 114: ! 115: if (bGrabMouse && !bInFullScreen) ! 116: /* Un-grab mouse pointer in windowed mode */ ! 117: SDL_WM_GrabInput(SDL_GRAB_OFF); ! 118: } ! 119: return true; ! 120: } ! 121: ! 122: /*-----------------------------------------------------------------------*/ ! 123: /** ! 124: * Start/continue emulation ! 125: * ! 126: * @return true if continued, false if was already running ! 127: */ ! 128: bool Main_UnPauseEmulation(void) ! 129: { ! 130: if ( bEmulationActive ) ! 131: return false; ! 132: ! 133: bEmulationActive = true; ! 134: ! 135: /* Cause full screen update (to clear all) */ ! 136: Screen_SetFullUpdate(); ! 137: ! 138: if (bGrabMouse) ! 139: /* Grab mouse pointer again */ ! 140: SDL_WM_GrabInput(SDL_GRAB_ON); ! 141: return true; ! 142: } ! 143: ! 144: /*-----------------------------------------------------------------------*/ ! 145: /** ! 146: * Optionally ask user whether to quit and set bQuitProgram accordingly ! 147: */ ! 148: void Main_RequestQuit(void) ! 149: { ! 150: if (ConfigureParams.Memory.bAutoSave) ! 151: { ! 152: bQuitProgram = true; ! 153: MemorySnapShot_Capture(ConfigureParams.Memory.szAutoSaveFileName, false); ! 154: } ! 155: else if (ConfigureParams.Log.bConfirmQuit) ! 156: { ! 157: bQuitProgram = false; /* if set true, dialog exits */ ! 158: bQuitProgram = DlgAlert_Query("All unsaved data will be lost.\nDo you really want to quit?"); ! 159: } ! 160: else ! 161: { ! 162: bQuitProgram = true; ! 163: } ! 164: ! 165: if (bQuitProgram) ! 166: { ! 167: /* Assure that CPU core shuts down */ ! 168: M68000_SetSpecial(SPCFLAG_BRK); ! 169: } ! 170: } ! 171: ! 172: /*-----------------------------------------------------------------------*/ ! 173: /** ! 174: * This function waits on each emulated VBL to synchronize the real time ! 175: * with the emulated ST. ! 176: * Unfortunately SDL_Delay and other sleep functions like usleep or nanosleep ! 177: * are very inaccurate on some systems like Linux 2.4 or Mac OS X (they can only ! 178: * wait for a multiple of 10ms due to the scheduler on these systems), so we have ! 179: * to "busy wait" there to get an accurate timing. ! 180: */ ! 181: void Main_WaitOnVbl(void) ! 182: { ! 183: int nCurrentMilliTicks; ! 184: static int nDestMilliTicks = 0; ! 185: int nFrameDuration; ! 186: signed int nDelay; ! 187: ! 188: nVBLCount++; ! 189: if (nRunVBLs && nVBLCount >= nRunVBLs) ! 190: { ! 191: /* show VBLs/s */ ! 192: Main_PauseEmulation(true); ! 193: exit(0); ! 194: } ! 195: nCurrentMilliTicks = SDL_GetTicks(); ! 196: ! 197: nFrameDuration = 1000/50; ! 198: nDelay = nDestMilliTicks - nCurrentMilliTicks; ! 199: ! 200: /* Do not wait if we are in fast forward mode or if we are totally out of sync */ ! 201: if (ConfigureParams.System.bFastForward == true ! 202: || nDelay < -4*nFrameDuration) ! 203: { ! 204: if (ConfigureParams.System.bFastForward == true) ! 205: { ! 206: if (!nFirstMilliTick) ! 207: nFirstMilliTick = Main_GetTicks(); ! 208: } ! 209: // if (nFrameSkips < ConfigureParams.Screen.nFrameSkips) ! 210: // { ! 211: // nFrameSkips += 1; ! 212: // Log_Printf(LOG_DEBUG, "Increased frameskip to %d\n", nFrameSkips); ! 213: // } ! 214: /* Only update nDestMilliTicks for next VBL */ ! 215: nDestMilliTicks = nCurrentMilliTicks + nFrameDuration; ! 216: return; ! 217: } ! 218: /* If automatic frameskip is enabled and delay's more than twice ! 219: * the effect of single frameskip, decrease frameskip ! 220: */ ! 221: // if (nFrameSkips > 0 ! 222: // && ConfigureParams.Screen.nFrameSkips >= AUTO_FRAMESKIP_LIMIT ! 223: // && 2*nDelay > nFrameDuration/nFrameSkips) ! 224: // { ! 225: // nFrameSkips -= 1; ! 226: // Log_Printf(LOG_DEBUG, "Decreased frameskip to %d\n", nFrameSkips); ! 227: // } ! 228: ! 229: if (bAccurateDelays) ! 230: { ! 231: /* Accurate sleeping is possible -> use SDL_Delay to free the CPU */ ! 232: if (nDelay > 1) ! 233: SDL_Delay(nDelay - 1); ! 234: } ! 235: else ! 236: { ! 237: /* No accurate SDL_Delay -> only wait if more than 5ms to go... */ ! 238: if (nDelay > 5) ! 239: SDL_Delay(nDelay<10 ? nDelay-1 : 9); ! 240: } ! 241: ! 242: /* Now busy-wait for the right tick: */ ! 243: while (nDelay > 0) ! 244: { ! 245: nCurrentMilliTicks = SDL_GetTicks(); ! 246: nDelay = nDestMilliTicks - nCurrentMilliTicks; ! 247: } ! 248: ! 249: /* Update nDestMilliTicks for next VBL */ ! 250: nDestMilliTicks += nFrameDuration; ! 251: } ! 252: ! 253: ! 254: /*-----------------------------------------------------------------------*/ ! 255: /** ! 256: * Since SDL_Delay and friends are very inaccurate on some systems, we have ! 257: * to check if we can rely on this delay function. ! 258: */ ! 259: static void Main_CheckForAccurateDelays(void) ! 260: { ! 261: int nStartTicks, nEndTicks; ! 262: ! 263: /* Force a task switch now, so we have a longer timeslice afterwards */ ! 264: SDL_Delay(10); ! 265: ! 266: nStartTicks = SDL_GetTicks(); ! 267: SDL_Delay(1); ! 268: nEndTicks = SDL_GetTicks(); ! 269: ! 270: /* If the delay took longer than 10ms, we are on an inaccurate system! */ ! 271: bAccurateDelays = ((nEndTicks - nStartTicks) < 9); ! 272: ! 273: if (bAccurateDelays) ! 274: Log_Printf(LOG_WARN, "Host system has accurate delays. (%d)\n", nEndTicks - nStartTicks); ! 275: else ! 276: Log_Printf(LOG_WARN, "Host system does not have accurate delays. (%d)\n", nEndTicks - nStartTicks); ! 277: } ! 278: ! 279: ! 280: /* ----------------------------------------------------------------------- */ ! 281: /** ! 282: * Set mouse pointer to new coordinates and set flag to ignore the mouse event ! 283: * that is generated by SDL_WarpMouse(). ! 284: */ ! 285: void Main_WarpMouse(int x, int y) ! 286: { ! 287: SDL_WarpMouse(x, y); /* Set mouse pointer to new position */ ! 288: bIgnoreNextMouseMotion = true; /* Ignore mouse motion event from SDL_WarpMouse */ ! 289: } ! 290: ! 291: ! 292: /* ----------------------------------------------------------------------- */ ! 293: /** ! 294: * Handle mouse motion event. ! 295: */ ! 296: static void Main_HandleMouseMotion(SDL_Event *pEvent) ! 297: { ! 298: int dx, dy; ! 299: static int ax = 0, ay = 0; ! 300: ! 301: /* Ignore motion when position has changed right after a reset or TOS ! 302: * (especially version 4.04) might get confused and play key clicks */ ! 303: if (bIgnoreNextMouseMotion ) ! 304: { ! 305: bIgnoreNextMouseMotion = false; ! 306: return; ! 307: } ! 308: ! 309: dx = pEvent->motion.xrel; ! 310: dy = pEvent->motion.yrel; ! 311: ! 312: /* In zoomed low res mode, we divide dx and dy by the zoom factor so that ! 313: * the ST mouse cursor stays in sync with the host mouse. However, we have ! 314: * to take care of lowest bit of dx and dy which will get lost when ! 315: * dividing. So we store these bits in ax and ay and add them to dx and dy ! 316: * the next time. */ ! 317: if (nScreenZoomX != 1) ! 318: { ! 319: dx += ax; ! 320: ax = dx % nScreenZoomX; ! 321: dx /= nScreenZoomX; ! 322: } ! 323: if (nScreenZoomY != 1) ! 324: { ! 325: dy += ay; ! 326: ay = dy % nScreenZoomY; ! 327: dy /= nScreenZoomY; ! 328: } ! 329: ! 330: } ! 331: ! 332: ! 333: /* ----------------------------------------------------------------------- */ ! 334: /** ! 335: * SDL message handler. ! 336: * Here we process the SDL events (keyboard, mouse, ...) and map it to ! 337: * Atari IKBD events. ! 338: */ ! 339: void Main_EventHandler(void) ! 340: { ! 341: bool bContinueProcessing; ! 342: SDL_Event event; ! 343: int events; ! 344: int remotepause; ! 345: ! 346: do ! 347: { ! 348: bContinueProcessing = false; ! 349: ! 350: /* check remote process control */ ! 351: remotepause = Control_CheckUpdates(); ! 352: ! 353: if ( bEmulationActive || remotepause ) ! 354: { ! 355: events = SDL_PollEvent(&event); ! 356: } ! 357: else ! 358: { ! 359: ShortCut_ActKey(); ! 360: /* last (shortcut) event activated emulation? */ ! 361: if ( bEmulationActive ) ! 362: break; ! 363: events = SDL_WaitEvent(&event); ! 364: } ! 365: if (!events) ! 366: { ! 367: /* no events -> if emulation is active or ! 368: * user is quitting -> return from function. ! 369: */ ! 370: continue; ! 371: } ! 372: switch (event.type) ! 373: { ! 374: ! 375: case SDL_QUIT: ! 376: Main_RequestQuit(); ! 377: break; ! 378: ! 379: case SDL_MOUSEMOTION: /* Read/Update internal mouse position */ ! 380: Main_HandleMouseMotion(&event); ! 381: bContinueProcessing = true; ! 382: break; ! 383: ! 384: case SDL_MOUSEBUTTONDOWN: ! 385: if (event.button.button == SDL_BUTTON_LEFT) ! 386: { ! 387: } ! 388: else if (event.button.button == SDL_BUTTON_RIGHT) ! 389: { ! 390: } ! 391: else if (event.button.button == SDL_BUTTON_MIDDLE) ! 392: { ! 393: } ! 394: else if (event.button.button == SDL_BUTTON_WHEELDOWN) ! 395: { ! 396: } ! 397: else if (event.button.button == SDL_BUTTON_WHEELUP) ! 398: { ! 399: } ! 400: break; ! 401: ! 402: case SDL_MOUSEBUTTONUP: ! 403: if (event.button.button == SDL_BUTTON_LEFT) ! 404: { ! 405: } ! 406: else if (event.button.button == SDL_BUTTON_RIGHT) ! 407: { ! 408: } ! 409: else if (event.button.button == SDL_BUTTON_WHEELDOWN) ! 410: { ! 411: } ! 412: else if (event.button.button == SDL_BUTTON_WHEELUP) ! 413: { ! 414: } ! 415: break; ! 416: ! 417: case SDL_KEYDOWN: ! 418: fprintf(stderr, "keydwn\n"); ! 419: Keymap_KeyDown(&event.key.keysym); ! 420: ShortCut_ActKey(); ! 421: break; ! 422: ! 423: case SDL_KEYUP: ! 424: Keymap_KeyUp(&event.key.keysym); ! 425: break; ! 426: ! 427: default: ! 428: /* don't let unknown events delay event processing */ ! 429: bContinueProcessing = true; ! 430: break; ! 431: } ! 432: } while (bContinueProcessing || !(bEmulationActive || bQuitProgram)); ! 433: } ! 434: ! 435: ! 436: /*-----------------------------------------------------------------------*/ ! 437: /** ! 438: * Initialise emulation ! 439: */ ! 440: static void Main_Init(void) ! 441: { ! 442: /* Open debug log file */ ! 443: if (!Log_Init()) ! 444: { ! 445: fprintf(stderr, "Logging/tracing initialization failed\n"); ! 446: exit(-1); ! 447: } ! 448: Log_Printf(LOG_INFO, PROG_NAME ", compiled on: " __DATE__ ", " __TIME__ "\n"); ! 449: ! 450: /* Init SDL's video subsystem. Note: Audio and joystick subsystems ! 451: will be initialized later (failures there are not fatal). */ ! 452: if (SDL_Init(SDL_INIT_VIDEO | Opt_GetNoParachuteFlag()) < 0) ! 453: { ! 454: fprintf(stderr, "Could not initialize the SDL library:\n %s\n", SDL_GetError() ); ! 455: exit(-1); ! 456: } ! 457: ! 458: SDLGui_Init(); ! 459: Screen_Init(); ! 460: M68000_Init(); /* Init CPU emulation */ ! 461: ! 462: if (Reset_Cold()) /* Reset all systems, load TOS image */ ! 463: { ! 464: /* If loading of the TOS failed, we bring up the GUI to let the ! 465: * user choose another TOS ROM file. */ ! 466: } ! 467: /* call menu at startup */ ! 468: Dialog_DoProperty(); ! 469: if (bQuitProgram) ! 470: { ! 471: fprintf(stderr, "Failed to load TOS image!\n"); ! 472: SDL_Quit(); ! 473: exit(-2); ! 474: } ! 475: ! 476: IoMem_Init(); ! 477: ! 478: /* done as last, needs CPU & DSP running... */ ! 479: DebugUI_Init(); ! 480: } ! 481: ! 482: ! 483: /*-----------------------------------------------------------------------*/ ! 484: /** ! 485: * Un-Initialise emulation ! 486: */ ! 487: static void Main_UnInit(void) ! 488: { ! 489: Screen_ReturnFromFullScreen(); ! 490: IoMem_UnInit(); ! 491: SDLGui_UnInit(); ! 492: Screen_UnInit(); ! 493: Exit680x0(); ! 494: ! 495: /* SDL uninit: */ ! 496: SDL_Quit(); ! 497: ! 498: /* Close debug log file */ ! 499: Log_UnInit(); ! 500: } ! 501: ! 502: ! 503: /*-----------------------------------------------------------------------*/ ! 504: /** ! 505: * Load initial configuration file(s) ! 506: */ ! 507: static void Main_LoadInitialConfig(void) ! 508: { ! 509: char *psGlobalConfig; ! 510: ! 511: psGlobalConfig = malloc(FILENAME_MAX); ! 512: if (psGlobalConfig) ! 513: { ! 514: #if defined(__AMIGAOS4__) ! 515: strncpy(psGlobalConfig, CONFDIR"previous.cfg", FILENAME_MAX); ! 516: #else ! 517: snprintf(psGlobalConfig, FILENAME_MAX, CONFDIR"%cprevious.cfg", PATHSEP); ! 518: #endif ! 519: /* Try to load the global configuration file */ ! 520: Configuration_Load(psGlobalConfig); ! 521: ! 522: free(psGlobalConfig); ! 523: } ! 524: ! 525: /* Now try the users configuration file */ ! 526: Configuration_Load(NULL); ! 527: } ! 528: ! 529: /*-----------------------------------------------------------------------*/ ! 530: /** ! 531: * Set TOS etc information and initial help message ! 532: */ ! 533: static void Main_StatusbarSetup(void) ! 534: { ! 535: const char *name = NULL; ! 536: SDLKey key; ! 537: ! 538: key = ConfigureParams.Shortcut.withoutModifier[SHORTCUT_OPTIONS]; ! 539: if (!key) ! 540: key = ConfigureParams.Shortcut.withModifier[SHORTCUT_OPTIONS]; ! 541: if (key) ! 542: name = SDL_GetKeyName(key); ! 543: if (name) ! 544: { ! 545: char message[24], *keyname; ! 546: #ifdef _MUDFLAP ! 547: __mf_register(name, 32, __MF_TYPE_GUESS, "SDL keyname"); ! 548: #endif ! 549: keyname = Str_ToUpper(strdup(name)); ! 550: snprintf(message, sizeof(message), "Press %s for Options", keyname); ! 551: free(keyname); ! 552: ! 553: Statusbar_AddMessage(message, 6000); ! 554: } ! 555: /* update information loaded by Main_Init() */ ! 556: Statusbar_UpdateInfo(); ! 557: } ! 558: ! 559: /*-----------------------------------------------------------------------*/ ! 560: /** ! 561: * Main ! 562: * ! 563: * Note: 'argv' cannot be declared const, MinGW would then fail to link. ! 564: */ ! 565: int main(int argc, char *argv[]) ! 566: { ! 567: /* Generate random seed */ ! 568: srand(time(NULL)); ! 569: ! 570: /* Initialize directory strings */ ! 571: Paths_Init(argv[0]); ! 572: ! 573: /* Set default configuration values: */ ! 574: Configuration_SetDefault(); ! 575: ! 576: /* Now load the values from the configuration file */ ! 577: Main_LoadInitialConfig(); ! 578: ! 579: /* Check for any passed parameters */ ! 580: if (!Opt_ParseParameters(argc, (const char**)argv)) ! 581: { ! 582: return 1; ! 583: } ! 584: /* monitor type option might require "reset" -> true */ ! 585: Configuration_Apply(true); ! 586: ! 587: #ifdef WIN32 ! 588: Win_OpenCon(); ! 589: #endif ! 590: ! 591: /* Needed on maemo but useful also with normal X11 window managers ! 592: * for window grouping when you have multiple Hatari SDL windows open ! 593: */ ! 594: #if HAVE_SETENV ! 595: setenv("SDL_VIDEO_X11_WMCLASS", "previous", 1); ! 596: #endif ! 597: ! 598: /* Init emulator system */ ! 599: Main_Init(); ! 600: ! 601: /* Set initial Statusbar information */ ! 602: Main_StatusbarSetup(); ! 603: ! 604: /* Check if SDL_Delay is accurate */ ! 605: Main_CheckForAccurateDelays(); ! 606: ! 607: ! 608: /* Run emulation */ ! 609: Main_UnPauseEmulation(); ! 610: M68000_Start(); /* Start emulation */ ! 611: ! 612: /* Un-init emulation system */ ! 613: Main_UnInit(); ! 614: ! 615: return 0; ! 616: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.