|
|
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.