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