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