|
|
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 <errno.h> ! 13: #include <signal.h> ! 14: ! 15: #include "main.h" ! 16: #include "configuration.h" ! 17: #include "dialog.h" ! 18: #include "ioMem.h" ! 19: #include "keymap.h" ! 20: #include "log.h" ! 21: #include "m68000.h" ! 22: #include "paths.h" ! 23: #include "reset.h" ! 24: #include "screen.h" ! 25: #include "sdlgui.h" ! 26: #include "shortcut.h" ! 27: #include "snd.h" ! 28: #include "statusbar.h" ! 29: #include "nextMemory.h" ! 30: #include "str.h" ! 31: #include "video.h" ! 32: #include "audio.h" ! 33: #include "debugui.h" ! 34: #include "file.h" ! 35: #include "dsp.h" ! 36: #include "host.h" ! 37: #include "dimension.hpp" ! 38: ! 39: #include "hatari-glue.h" ! 40: #include "NextBus.hpp" ! 41: ! 42: #if HAVE_GETTIMEOFDAY ! 43: #include <sys/time.h> ! 44: #endif ! 45: ! 46: int nFrameSkips; ! 47: ! 48: bool bQuitProgram = false; /* Flag to quit program cleanly */ ! 49: ! 50: static bool bEmulationActive = true; /* Run emulation when started */ ! 51: static bool bAccurateDelays; /* Host system has an accurate SDL_Delay()? */ ! 52: static bool bIgnoreNextMouseMotion = false; /* Next mouse motion will be ignored (needed after SDL_WarpMouse) */ ! 53: ! 54: volatile int mainPauseEmulation; ! 55: ! 56: typedef const char* (*report_func)(double realTime, double hostTime); ! 57: ! 58: typedef struct { ! 59: const char* label; ! 60: const report_func report; ! 61: } report_t; ! 62: ! 63: static double lastRT; ! 64: static Uint64 lastCycles; ! 65: static double speedFactor; ! 66: static char speedMsg[32]; ! 67: ! 68: static void Main_Speed(double realTime, double hostTime) { ! 69: double dRT = realTime - lastRT; ! 70: speedFactor = nCyclesMainCounter - lastCycles; ! 71: speedFactor /= ConfigureParams.System.nCpuFreq; ! 72: speedFactor /= 1000 * 1000; ! 73: speedFactor /= dRT; ! 74: lastRT = realTime; ! 75: lastCycles = nCyclesMainCounter; ! 76: } ! 77: ! 78: void Main_SpeedReset(void) { ! 79: double realTime, hostTime; ! 80: host_time(&realTime, &hostTime); ! 81: lastRT = realTime; ! 82: lastCycles = nCyclesMainCounter; ! 83: ! 84: Log_Printf(LOG_WARN, "Realtime mode %s.\n", ConfigureParams.System.bRealtime ? "enabled" : "disabled"); ! 85: } ! 86: ! 87: const char* Main_SpeedMsg() { ! 88: speedMsg[0] = 0; ! 89: if(speedFactor > 0) { ! 90: if(ConfigureParams.System.bRealtime) { ! 91: sprintf(speedMsg, "%dMHz/", (int)(ConfigureParams.System.nCpuFreq * speedFactor + 0.5)); ! 92: } else { ! 93: if ((speedFactor < 0.9) || (speedFactor > 1.1)) ! 94: sprintf(speedMsg, "%.1fx%dMHz/", speedFactor, ConfigureParams.System.nCpuFreq); ! 95: else ! 96: sprintf(speedMsg, "%dMHz/", ConfigureParams.System.nCpuFreq); ! 97: } ! 98: } ! 99: return speedMsg; ! 100: } ! 101: ! 102: #if ENABLE_TESTING ! 103: static const report_t reports[] = { ! 104: {"ND", nd_reports}, ! 105: {"Host", host_report}, ! 106: }; ! 107: #endif ! 108: ! 109: /*-----------------------------------------------------------------------*/ ! 110: /** ! 111: * Pause emulation, stop sound. 'visualize' should be set true, ! 112: * unless unpause will be called immediately afterwards. ! 113: * ! 114: * @return true if paused now, false if was already paused ! 115: */ ! 116: bool Main_PauseEmulation(bool visualize) { ! 117: if ( !bEmulationActive ) ! 118: return false; ! 119: ! 120: bEmulationActive = false; ! 121: host_pause_time(!(bEmulationActive)); ! 122: Screen_Pause(true); ! 123: Sound_Pause(true); ! 124: NextBus_Pause(true); ! 125: ! 126: if (visualize) { ! 127: Statusbar_AddMessage("Emulation paused", 100); ! 128: /* make sure msg gets shown */ ! 129: Statusbar_Update(sdlscrn); ! 130: ! 131: if (bGrabMouse && !bInFullScreen) { ! 132: /* Un-grab mouse pointer in windowed mode */ ! 133: SDL_SetRelativeMouseMode(SDL_FALSE); ! 134: SDL_SetWindowGrab(sdlWindow, SDL_FALSE); ! 135: } ! 136: } ! 137: return true; ! 138: } ! 139: ! 140: /*-----------------------------------------------------------------------*/ ! 141: /** ! 142: * Start/continue emulation ! 143: * ! 144: * @return true if continued, false if was already running ! 145: */ ! 146: bool Main_UnPauseEmulation(void) { ! 147: if ( bEmulationActive ) ! 148: return false; ! 149: ! 150: bEmulationActive = true; ! 151: host_pause_time(!(bEmulationActive)); ! 152: Screen_Pause(false); ! 153: Sound_Pause(false); ! 154: NextBus_Pause(false); ! 155: ! 156: if (bGrabMouse) { ! 157: /* Grab mouse pointer again */ ! 158: SDL_SetRelativeMouseMode(SDL_TRUE); ! 159: SDL_SetWindowGrab(sdlWindow, SDL_TRUE); ! 160: } ! 161: return true; ! 162: } ! 163: ! 164: /*-----------------------------------------------------------------------*/ ! 165: /** ! 166: * Optionally ask user whether to quit and set bQuitProgram accordingly ! 167: */ ! 168: void Main_RequestQuit(void) { ! 169: if (ConfigureParams.Log.bConfirmQuit) { ! 170: bQuitProgram = false; /* if set true, dialog exits */ ! 171: bQuitProgram = DlgAlert_Query("All unsaved data will be lost.\nDo you really want to quit?"); ! 172: } ! 173: else { ! 174: bQuitProgram = true; ! 175: } ! 176: ! 177: if (bQuitProgram) { ! 178: /* Assure that CPU core shuts down */ ! 179: M68000_SetSpecial(SPCFLAG_BRK); ! 180: } ! 181: } ! 182: ! 183: /*-----------------------------------------------------------------------*/ ! 184: /** ! 185: * Since SDL_Delay and friends are very inaccurate on some systems, we have ! 186: * to check if we can rely on this delay function. ! 187: */ ! 188: static void Main_CheckForAccurateDelays(void) { ! 189: int nStartTicks, nEndTicks; ! 190: ! 191: /* Force a task switch now, so we have a longer timeslice afterwards */ ! 192: SDL_Delay(10); ! 193: ! 194: nStartTicks = SDL_GetTicks(); ! 195: SDL_Delay(1); ! 196: nEndTicks = SDL_GetTicks(); ! 197: ! 198: /* If the delay took longer than 10ms, we are on an inaccurate system! */ ! 199: bAccurateDelays = ((nEndTicks - nStartTicks) < 9); ! 200: ! 201: if (bAccurateDelays) ! 202: Log_Printf(LOG_WARN, "Host system has accurate delays. (%d)\n", nEndTicks - nStartTicks); ! 203: else ! 204: Log_Printf(LOG_WARN, "Host system does not have accurate delays. (%d)\n", nEndTicks - nStartTicks); ! 205: } ! 206: ! 207: ! 208: /* ----------------------------------------------------------------------- */ ! 209: /** ! 210: * Set mouse pointer to new coordinates and set flag to ignore the mouse event ! 211: * that is generated by SDL_WarpMouse(). ! 212: */ ! 213: void Main_WarpMouse(int x, int y) { ! 214: SDL_WarpMouseInWindow(sdlWindow, x, y); /* Set mouse pointer to new position */ ! 215: bIgnoreNextMouseMotion = true; /* Ignore mouse motion event from SDL_WarpMouse */ ! 216: } ! 217: ! 218: ! 219: /* ----------------------------------------------------------------------- */ ! 220: /** ! 221: * Handle mouse motion event. ! 222: */ ! 223: SDL_Event mymouse[100]; ! 224: static void Main_HandleMouseMotion(SDL_Event *pEvent) { ! 225: int dx, dy; ! 226: int i,nb; ! 227: ! 228: dx = pEvent->motion.xrel; ! 229: dy = pEvent->motion.yrel; ! 230: ! 231: /* get all mouse event to clean the queue and sum them */ ! 232: nb=SDL_PeepEvents(&mymouse[0], 100, SDL_GETEVENT, SDL_MOUSEMOTION, SDL_MOUSEMOTION); ! 233: ! 234: for (i=0;i<nb;i++) { ! 235: dx += mymouse[i].motion.xrel; ! 236: dy += mymouse[i].motion.yrel; ! 237: } ! 238: ! 239: if (bGrabMouse) { ! 240: Keymap_MouseMove(dx,dy,ConfigureParams.Mouse.fLinSpeedLocked,ConfigureParams.Mouse.fExpSpeedLocked); ! 241: } else { ! 242: Keymap_MouseMove(dx,dy,ConfigureParams.Mouse.fLinSpeedNormal,ConfigureParams.Mouse.fLinSpeedNormal); ! 243: } ! 244: } ! 245: ! 246: static int statusBarUpdate; ! 247: ! 248: /* ----------------------------------------------------------------------- */ ! 249: /** ! 250: * SDL message handler. ! 251: * Here we process the SDL events (keyboard, mouse, ...) ! 252: */ ! 253: void Main_EventHandler(void) { ! 254: bool bContinueProcessing; ! 255: SDL_Event event; ! 256: int events; ! 257: ! 258: if(++statusBarUpdate > 400) { ! 259: double vt; ! 260: double rt; ! 261: host_time(&rt, &vt); ! 262: #if ENABLE_TESTING ! 263: fprintf(stderr, "[reports]"); ! 264: for(int i = 0; i < sizeof(reports)/sizeof(report_t); i++) { ! 265: const char* msg = reports[i].report(rt, vt); ! 266: if(msg[0]) fprintf(stderr, " %s:%s", reports[i].label, msg); ! 267: } ! 268: fprintf(stderr, "\n"); ! 269: #else ! 270: Main_Speed(rt, vt); ! 271: #endif ! 272: Statusbar_UpdateInfo(); ! 273: statusBarUpdate = 0; ! 274: } ! 275: ! 276: do { ! 277: bContinueProcessing = false; ! 278: ! 279: /* check remote process control from different thread (e.g. i860) */ ! 280: switch(mainPauseEmulation) { ! 281: case PAUSE_EMULATION: ! 282: mainPauseEmulation = PAUSE_NONE; ! 283: Main_PauseEmulation(true); ! 284: break; ! 285: case UNPAUSE_EMULATION: ! 286: mainPauseEmulation = PAUSE_NONE; ! 287: Main_UnPauseEmulation(); ! 288: break; ! 289: } ! 290: ! 291: if (bEmulationActive) { ! 292: double time_offset = host_real_time_offset() * 1000; ! 293: if(time_offset > 10) ! 294: events = SDL_WaitEventTimeout(&event, time_offset); ! 295: else ! 296: events = SDL_PollEvent(&event); ! 297: } ! 298: else { ! 299: ShortCut_ActKey(); ! 300: /* last (shortcut) event activated emulation? */ ! 301: if ( bEmulationActive ) ! 302: break; ! 303: events = SDL_WaitEvent(&event); ! 304: } ! 305: if (!events) { ! 306: /* no events -> if emulation is active or ! 307: * user is quitting -> return from function. ! 308: */ ! 309: continue; ! 310: } ! 311: switch (event.type) { ! 312: case SDL_WINDOWEVENT: ! 313: if(event.window.event == SDL_WINDOWEVENT_CLOSE) { ! 314: SDL_WaitEventTimeout(&event, 100); // grab SDL_Quit if pending ! 315: Main_RequestQuit(); ! 316: } ! 317: continue; ! 318: ! 319: case SDL_QUIT: ! 320: Main_RequestQuit(); ! 321: break; ! 322: ! 323: case SDL_MOUSEMOTION: /* Read/Update internal mouse position */ ! 324: Main_HandleMouseMotion(&event); ! 325: bContinueProcessing = false; ! 326: break; ! 327: ! 328: case SDL_MOUSEBUTTONDOWN: ! 329: if (event.button.button == SDL_BUTTON_LEFT) { ! 330: if (ConfigureParams.Mouse.bEnableAutoGrab && !bGrabMouse) { ! 331: bGrabMouse = true; /* Toggle flag */ ! 332: ! 333: /* If we are in windowed mode, toggle the mouse cursor mode now: */ ! 334: if (!bInFullScreen) ! 335: { ! 336: SDL_SetRelativeMouseMode(SDL_TRUE); ! 337: SDL_SetWindowGrab(sdlWindow, SDL_TRUE); ! 338: Main_SetTitle(MOUSE_LOCK_MSG); ! 339: } ! 340: } ! 341: ! 342: Keymap_MouseDown(true); ! 343: } ! 344: else if (event.button.button == SDL_BUTTON_RIGHT) ! 345: { ! 346: Keymap_MouseDown(false); ! 347: } ! 348: break; ! 349: ! 350: case SDL_MOUSEBUTTONUP: ! 351: if (event.button.button == SDL_BUTTON_LEFT) { ! 352: Keymap_MouseUp(true); ! 353: } ! 354: else if (event.button.button == SDL_BUTTON_RIGHT) ! 355: { ! 356: Keymap_MouseUp(false); ! 357: } ! 358: break; ! 359: ! 360: case SDL_MOUSEWHEEL: ! 361: Keymap_MouseWheel(&event.wheel); ! 362: break; ! 363: ! 364: case SDL_KEYDOWN: ! 365: if (event.key.repeat) ! 366: break; ! 367: ! 368: Keymap_KeyDown(&event.key.keysym); ! 369: break; ! 370: ! 371: case SDL_KEYUP: ! 372: Keymap_KeyUp(&event.key.keysym); ! 373: break; ! 374: ! 375: ! 376: default: ! 377: /* don't let unknown events delay event processing */ ! 378: bContinueProcessing = true; ! 379: break; ! 380: } ! 381: } while (bContinueProcessing || !(bEmulationActive || bQuitProgram)); ! 382: } ! 383: ! 384: ! 385: void Main_EventHandlerInterrupt() { ! 386: CycInt_AcknowledgeInterrupt(); ! 387: Main_EventHandler(); ! 388: CycInt_AddRelativeInterruptUs((1000*1000)/200, 0, INTERRUPT_EVENT_LOOP); // poll events with 200 Hz ! 389: } ! 390: ! 391: /*-----------------------------------------------------------------------*/ ! 392: /** ! 393: * Set Hatari window title. Use NULL for default ! 394: */ ! 395: void Main_SetTitle(const char *title) { ! 396: if (title) ! 397: SDL_SetWindowTitle(sdlWindow, title); ! 398: else ! 399: SDL_SetWindowTitle(sdlWindow, PROG_NAME); ! 400: } ! 401: ! 402: /*-----------------------------------------------------------------------*/ ! 403: /** ! 404: * Initialise emulation ! 405: */ ! 406: static void Main_Init(void) { ! 407: /* Open debug log file */ ! 408: if (!Log_Init()) { ! 409: fprintf(stderr, "Logging/tracing initialization failed\n"); ! 410: exit(-1); ! 411: } ! 412: Log_Printf(LOG_INFO, PROG_NAME ", compiled on: " __DATE__ ", " __TIME__ "\n"); ! 413: ! 414: /* Init SDL's video subsystem. Note: Audio and joystick subsystems ! 415: will be initialized later (failures there are not fatal). */ ! 416: if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) < 0) ! 417: { ! 418: fprintf(stderr, "Could not initialize the SDL library:\n %s\n", SDL_GetError() ); ! 419: exit(-1); ! 420: } ! 421: SDLGui_Init(); ! 422: Screen_Init(); ! 423: Main_SetTitle(NULL); ! 424: DSP_Init(); ! 425: M68000_Init(); /* Init CPU emulation */ ! 426: Keymap_Init(); ! 427: ! 428: /* call menu at startup */ ! 429: if (!File_Exists(sConfigFileName) || ConfigureParams.ConfigDialog.bShowConfigDialogAtStartup) { ! 430: Dialog_DoProperty(); ! 431: if (bQuitProgram) { ! 432: SDL_Quit(); ! 433: exit(-2); ! 434: } ! 435: } ! 436: ! 437: Dialog_CheckFiles(); ! 438: ! 439: if (bQuitProgram) { ! 440: SDL_Quit(); ! 441: exit(-2); ! 442: } ! 443: ! 444: Reset_Cold(); ! 445: ! 446: IoMem_Init(); ! 447: ! 448: /* Start EventHandler */ ! 449: CycInt_AddRelativeInterruptUs(500*1000, 0, INTERRUPT_EVENT_LOOP); ! 450: ! 451: /* done as last, needs CPU & DSP running... */ ! 452: DebugUI_Init(); ! 453: } ! 454: ! 455: ! 456: /*-----------------------------------------------------------------------*/ ! 457: /** ! 458: * Un-Initialise emulation ! 459: */ ! 460: static void Main_UnInit(void) { ! 461: Screen_ReturnFromFullScreen(); ! 462: IoMem_UnInit(); ! 463: SDLGui_UnInit(); ! 464: Screen_UnInit(); ! 465: Exit680x0(); ! 466: ! 467: /* SDL uninit: */ ! 468: SDL_Quit(); ! 469: ! 470: /* Close debug log file */ ! 471: Log_UnInit(); ! 472: } ! 473: ! 474: ! 475: /*-----------------------------------------------------------------------*/ ! 476: /** ! 477: * Load initial configuration file(s) ! 478: */ ! 479: static void Main_LoadInitialConfig(void) { ! 480: char *psGlobalConfig; ! 481: ! 482: psGlobalConfig = malloc(FILENAME_MAX); ! 483: if (psGlobalConfig) ! 484: { ! 485: #if defined(__AMIGAOS4__) ! 486: strncpy(psGlobalConfig, CONFDIR"previous.cfg", FILENAME_MAX); ! 487: #else ! 488: snprintf(psGlobalConfig, FILENAME_MAX, CONFDIR"%cprevious.cfg", PATHSEP); ! 489: #endif ! 490: /* Try to load the global configuration file */ ! 491: Configuration_Load(psGlobalConfig); ! 492: ! 493: free(psGlobalConfig); ! 494: } ! 495: ! 496: /* Now try the users configuration file */ ! 497: Configuration_Load(NULL); ! 498: } ! 499: ! 500: /*-----------------------------------------------------------------------*/ ! 501: /** ! 502: * Set TOS etc information and initial help message ! 503: */ ! 504: static void Main_StatusbarSetup(void) { ! 505: const char *name = NULL; ! 506: SDL_Keycode key; ! 507: ! 508: key = ConfigureParams.Shortcut.withoutModifier[SHORTCUT_OPTIONS]; ! 509: if (!key) ! 510: key = ConfigureParams.Shortcut.withModifier[SHORTCUT_OPTIONS]; ! 511: if (key) ! 512: name = SDL_GetKeyName(key); ! 513: if (name) ! 514: { ! 515: char message[24], *keyname; ! 516: #ifdef _MUDFLAP ! 517: __mf_register(name, 32, __MF_TYPE_GUESS, "SDL keyname"); ! 518: #endif ! 519: keyname = Str_ToUpper(strdup(name)); ! 520: snprintf(message, sizeof(message), "Press %s for Options", keyname); ! 521: free(keyname); ! 522: ! 523: Statusbar_AddMessage(message, 6000); ! 524: } ! 525: /* update information loaded by Main_Init() */ ! 526: Statusbar_UpdateInfo(); ! 527: } ! 528: ! 529: #ifdef WIN32 ! 530: extern void Win_OpenCon(void); ! 531: #endif ! 532: ! 533: /*-----------------------------------------------------------------------*/ ! 534: /** ! 535: * Set signal handlers to catch signals ! 536: */ ! 537: static void Main_SetSignalHandlers(void) { ! 538: #ifndef _WIN32 ! 539: signal(SIGPIPE, SIG_IGN); ! 540: #endif ! 541: signal(SIGFPE, SIG_IGN); ! 542: } ! 543: ! 544: ! 545: /*-----------------------------------------------------------------------*/ ! 546: /** ! 547: * Main ! 548: * ! 549: * Note: 'argv' cannot be declared const, MinGW would then fail to link. ! 550: */ ! 551: int main(int argc, char *argv[]) { ! 552: /* Generate random seed */ ! 553: srand(time(NULL)); ! 554: ! 555: /* Set signal handlers */ ! 556: Main_SetSignalHandlers(); ! 557: ! 558: /* Initialize directory strings */ ! 559: Paths_Init(argv[0]); ! 560: ! 561: /* Set default configuration values: */ ! 562: Configuration_SetDefault(); ! 563: ! 564: /* Now load the values from the configuration file */ ! 565: Main_LoadInitialConfig(); ! 566: ! 567: #if 0 /* FIXME: This sometimes causes exits when starting from application bundles */ ! 568: /* Check for any passed parameters */ ! 569: if (!Opt_ParseParameters(argc, (const char * const *)argv)) ! 570: { ! 571: return 1; ! 572: } ! 573: #endif ! 574: /* monitor type option might require "reset" -> true */ ! 575: Configuration_Apply(true); ! 576: ! 577: #ifdef WIN32 ! 578: Win_OpenCon(); ! 579: #endif ! 580: ! 581: /* Needed on maemo but useful also with normal X11 window managers ! 582: * for window grouping when you have multiple Hatari SDL windows open ! 583: */ ! 584: #if HAVE_SETENV ! 585: setenv("SDL_VIDEO_X11_WMCLASS", "previous", 1); ! 586: #endif ! 587: ! 588: /* Init emulator system */ ! 589: Main_Init(); ! 590: ! 591: /* Set initial Statusbar information */ ! 592: Main_StatusbarSetup(); ! 593: ! 594: /* Check if SDL_Delay is accurate */ ! 595: Main_CheckForAccurateDelays(); ! 596: ! 597: ! 598: /* Run emulation */ ! 599: Main_UnPauseEmulation(); ! 600: M68000_Start(); /* Start emulation */ ! 601: ! 602: ! 603: /* Un-init emulation system */ ! 604: Main_UnInit(); ! 605: ! 606: return 0; ! 607: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.