|
|
1.1 root 1: /*
1.1.1.6 root 2: Hatari - main.c
3:
1.1.1.22 root 4: This file is distributed under the GNU General Public License, version 2
5: or at your option any later version. Read the file gpl.txt for details.
1.1.1.6 root 6:
7: Main initialization and event handling routines.
1.1 root 8: */
1.1.1.17 root 9: const char Main_fileid[] = "Hatari main.c : " __DATE__ " " __TIME__;
1.1 root 10:
1.1.1.13 root 11: #include <time.h>
1.1.1.19 root 12: #include <errno.h>
1.1 root 13: #include <SDL.h>
14:
15: #include "main.h"
1.1.1.22 root 16: #include "version.h"
1.1 root 17: #include "configuration.h"
1.1.1.15 root 18: #include "control.h"
1.1.1.12 root 19: #include "options.h"
1.1 root 20: #include "dialog.h"
21: #include "audio.h"
22: #include "joy.h"
23: #include "floppy.h"
1.1.1.23 root 24: #include "floppy_ipf.h"
1.1.1.24! root 25: #include "floppy_stx.h"
1.1 root 26: #include "gemdos.h"
1.1.1.23 root 27: #include "fdc.h"
1.1.1.4 root 28: #include "hdc.h"
1.1.1.16 root 29: #include "ide.h"
1.1.1.22 root 30: #include "acia.h"
1.1 root 31: #include "ikbd.h"
1.1.1.10 root 32: #include "ioMem.h"
1.1.1.5 root 33: #include "keymap.h"
1.1.1.10 root 34: #include "log.h"
1.1 root 35: #include "m68000.h"
36: #include "memorySnapShot.h"
1.1.1.8 root 37: #include "midi.h"
1.1.1.13 root 38: #include "nvram.h"
39: #include "paths.h"
1.1 root 40: #include "printer.h"
1.1.1.8 root 41: #include "reset.h"
1.1.1.19 root 42: #include "resolution.h"
1.1 root 43: #include "rs232.h"
44: #include "screen.h"
1.1.1.5 root 45: #include "sdlgui.h"
1.1 root 46: #include "shortcut.h"
47: #include "sound.h"
1.1.1.18 root 48: #include "dmaSnd.h"
1.1.1.15 root 49: #include "statusbar.h"
1.1.1.8 root 50: #include "stMemory.h"
1.1.1.15 root 51: #include "str.h"
1.1 root 52: #include "tos.h"
53: #include "video.h"
1.1.1.18 root 54: #include "avi_record.h"
55: #include "debugui.h"
1.1.1.19 root 56: #include "clocks_timings.h"
1.1 root 57:
1.1.1.13 root 58: #include "hatari-glue.h"
59:
60: #include "falcon/hostscreen.h"
61: #include "falcon/dsp.h"
1.1 root 62:
1.1.1.19 root 63: #if HAVE_GETTIMEOFDAY
64: #include <sys/time.h>
65: #endif
66:
1.1.1.23 root 67: #ifdef WIN32
68: #include "gui-win/opencon.h"
69: #endif
1.1 root 70:
1.1.1.17 root 71: bool bQuitProgram = false; /* Flag to quit program cleanly */
1.1.1.23 root 72: static int nQuitValue; /* exit value */
1.1.1.13 root 73:
1.1.1.19 root 74: static Uint32 nRunVBLs; /* Whether and how many VBLS to run before exit */
1.1.1.17 root 75: static Uint32 nFirstMilliTick; /* Ticks when VBL counting started */
76: static Uint32 nVBLCount; /* Frame count */
1.1.1.23 root 77: static int nVBLSlowdown = 1; /* host VBL wait multiplier */
1.1.1.17 root 78:
79: static bool bEmulationActive = true; /* Run emulation when started */
1.1.1.15 root 80: static bool bAccurateDelays; /* Host system has an accurate SDL_Delay()? */
1.1.1.17 root 81: static bool bIgnoreNextMouseMotion = false; /* Next mouse motion will be ignored (needed after SDL_WarpMouse) */
1.1 root 82:
83: /*-----------------------------------------------------------------------*/
1.1.1.13 root 84: /**
1.1.1.17 root 85: * Return current time as millisecond for performance measurements.
86: *
87: * (On Unix only time spent by Hatari itself is counted, on other
88: * platforms less accurate SDL "wall clock".)
89: */
90: #if HAVE_SYS_TIMES_H
91: #include <unistd.h>
92: #include <sys/times.h>
93: static Uint32 Main_GetTicks(void)
94: {
95: static unsigned int ticks_to_msec = 0;
96: struct tms fields;
97: if (!ticks_to_msec)
98: {
99: ticks_to_msec = sysconf(_SC_CLK_TCK);
100: printf("OS clock ticks / second: %d\n", ticks_to_msec);
101: /* Linux has 100Hz virtual clock so no accuracy loss there */
102: ticks_to_msec = 1000UL / ticks_to_msec;
103: }
104: /* return milliseconds (clock ticks) spent in this process
105: */
106: times(&fields);
107: return ticks_to_msec * fields.tms_utime;
108: }
109: #else
1.1.1.18 root 110: # warning "times() function missing, using inaccurate SDL_GetTicks() instead."
111: # define Main_GetTicks SDL_GetTicks
1.1.1.17 root 112: #endif
113:
114:
1.1.1.19 root 115: //#undef HAVE_GETTIMEOFDAY
116: //#undef HAVE_NANOSLEEP
117:
118: /*-----------------------------------------------------------------------*/
119: /**
120: * Return a time counter in micro seconds.
121: * If gettimeofday is available, we use it directly, else we convert the
122: * return of SDL_GetTicks in micro sec.
123: */
124:
125: static Sint64 Time_GetTicks ( void )
126: {
1.1.1.23 root 127: Sint64 ticks_micro;
1.1.1.19 root 128:
129: #if HAVE_GETTIMEOFDAY
1.1.1.23 root 130: struct timeval now;
131: gettimeofday ( &now , NULL );
132: ticks_micro = (Sint64)now.tv_sec * 1000000 + now.tv_usec;
1.1.1.19 root 133: #else
134: ticks_micro = (Sint64)SDL_GetTicks() * 1000; /* milli sec -> micro sec */
135: #endif
136:
137: return ticks_micro;
138: }
139:
140:
141: /*-----------------------------------------------------------------------*/
142: /**
143: * Sleep for a given number of micro seconds.
144: * If nanosleep is available, we use it directly, else we use SDL_Delay
145: * (which is portable, but less accurate as is uses milli-seconds)
146: */
147:
148: static void Time_Delay ( Sint64 ticks_micro )
149: {
150: #if HAVE_NANOSLEEP
151: struct timespec ts;
152: int ret;
153: ts.tv_sec = ticks_micro / 1000000;
154: ts.tv_nsec = (ticks_micro % 1000000) * 1000; /* micro sec -> nano sec */
155: /* wait until all the delay is elapsed, including possible interruptions by signals */
156: do
157: {
158: errno = 0;
159: ret = nanosleep(&ts, &ts);
160: } while ( ret && ( errno == EINTR ) ); /* keep on sleeping if we were interrupted */
161: #else
162: SDL_Delay ( (Uint32)(ticks_micro / 1000) ) ; /* micro sec -> milli sec */
163: #endif
164: }
165:
166:
1.1.1.17 root 167: /*-----------------------------------------------------------------------*/
168: /**
169: * Pause emulation, stop sound. 'visualize' should be set true,
1.1.1.15 root 170: * unless unpause will be called immediately afterwards.
171: *
1.1.1.17 root 172: * @return true if paused now, false if was already paused
1.1.1.13 root 173: */
1.1.1.15 root 174: bool Main_PauseEmulation(bool visualize)
1.1 root 175: {
1.1.1.15 root 176: if ( !bEmulationActive )
1.1.1.17 root 177: return false;
1.1.1.15 root 178:
1.1.1.17 root 179: Audio_EnableAudio(false);
180: bEmulationActive = false;
1.1.1.15 root 181: if (visualize)
1.1.1.13 root 182: {
1.1.1.17 root 183: if (nFirstMilliTick)
184: {
185: int interval = Main_GetTicks() - nFirstMilliTick;
186: static float previous;
187: float current;
188:
189: current = (1000.0 * nVBLCount) / interval;
190: printf("SPEED: %.1f VBL/s (%d/%.1fs), diff=%.1f%%\n",
191: current, nVBLCount, interval/1000.0,
1.1.1.19 root 192: previous>0.0 ? 100*(current-previous)/previous : 0.0);
1.1.1.17 root 193: nVBLCount = nFirstMilliTick = 0;
194: previous = current;
195: }
196:
1.1.1.15 root 197: Statusbar_AddMessage("Emulation paused", 100);
198: /* make sure msg gets shown */
1.1.1.23 root 199: Statusbar_Update(sdlscrn, true);
1.1.1.17 root 200:
201: if (bGrabMouse && !bInFullScreen)
202: /* Un-grab mouse pointer in windowed mode */
203: SDL_WM_GrabInput(SDL_GRAB_OFF);
1.1.1.13 root 204: }
1.1.1.17 root 205: return true;
1.1 root 206: }
207:
1.1.1.2 root 208: /*-----------------------------------------------------------------------*/
1.1.1.13 root 209: /**
1.1.1.15 root 210: * Start/continue emulation
211: *
1.1.1.17 root 212: * @return true if continued, false if was already running
1.1.1.13 root 213: */
1.1.1.15 root 214: bool Main_UnPauseEmulation(void)
1.1 root 215: {
1.1.1.15 root 216: if ( bEmulationActive )
1.1.1.17 root 217: return false;
1.1 root 218:
1.1.1.19 root 219: Sound_BufferIndexNeedReset = true;
1.1.1.15 root 220: Audio_EnableAudio(ConfigureParams.Sound.bEnableSound);
1.1.1.17 root 221: bEmulationActive = true;
1.1.1.15 root 222:
223: /* Cause full screen update (to clear all) */
224: Screen_SetFullUpdate();
1.1.1.17 root 225:
226: if (bGrabMouse)
227: /* Grab mouse pointer again */
228: SDL_WM_GrabInput(SDL_GRAB_ON);
229: return true;
1.1 root 230: }
231:
1.1.1.13 root 232: /*-----------------------------------------------------------------------*/
233: /**
234: * Optionally ask user whether to quit and set bQuitProgram accordingly
235: */
1.1.1.23 root 236: void Main_RequestQuit(int exitval)
1.1.1.13 root 237: {
238: if (ConfigureParams.Memory.bAutoSave)
239: {
1.1.1.17 root 240: bQuitProgram = true;
241: MemorySnapShot_Capture(ConfigureParams.Memory.szAutoSaveFileName, false);
1.1.1.13 root 242: }
243: else if (ConfigureParams.Log.bConfirmQuit)
244: {
1.1.1.17 root 245: bQuitProgram = false; /* if set true, dialog exits */
1.1.1.13 root 246: bQuitProgram = DlgAlert_Query("All unsaved data will be lost.\nDo you really want to quit?");
247: }
248: else
249: {
1.1.1.17 root 250: bQuitProgram = true;
1.1.1.13 root 251: }
252:
253: if (bQuitProgram)
254: {
255: /* Assure that CPU core shuts down */
256: M68000_SetSpecial(SPCFLAG_BRK);
257: }
1.1.1.23 root 258: nQuitValue = exitval;
1.1.1.13 root 259: }
1.1.1.7 root 260:
1.1.1.12 root 261: /*-----------------------------------------------------------------------*/
1.1.1.13 root 262: /**
1.1.1.19 root 263: * Set how many VBLs Hatari should run, from the moment this function
264: * is called.
265: */
266: void Main_SetRunVBLs(Uint32 vbls)
267: {
268: fprintf(stderr, "Exit after %d VBLs.\n", vbls);
269: nRunVBLs = vbls;
270: nVBLCount = 0;
271: }
272:
273: /*-----------------------------------------------------------------------*/
274: /**
1.1.1.23 root 275: * Set VBL wait slowdown factor/multiplayer
276: */
277: bool Main_SetVBLSlowdown(int factor)
278: {
279: if (factor < 1 || factor > 8) {
280: fprintf(stderr, "ERROR: invalid VBL slowdown factor %d, should be 1-8!\n", factor);
281: return false;
282: }
283: fprintf(stderr, "Slow down host VBL wait by factor of %d.\n", factor);
284: nVBLSlowdown = factor;
285: return true;
286: }
287:
288: /*-----------------------------------------------------------------------*/
289: /**
1.1.1.13 root 290: * This function waits on each emulated VBL to synchronize the real time
291: * with the emulated ST.
292: * Unfortunately SDL_Delay and other sleep functions like usleep or nanosleep
293: * are very inaccurate on some systems like Linux 2.4 or Mac OS X (they can only
294: * wait for a multiple of 10ms due to the scheduler on these systems), so we have
295: * to "busy wait" there to get an accurate timing.
1.1.1.19 root 296: * All times are expressed as micro seconds, to avoid too much rounding error.
1.1.1.13 root 297: */
1.1.1.12 root 298: void Main_WaitOnVbl(void)
299: {
1.1.1.19 root 300: Sint64 CurrentTicks;
301: static Sint64 DestTicks = 0;
302: Sint64 FrameDuration_micro;
303: Sint64 nDelay;
1.1.1.13 root 304:
1.1.1.18 root 305: nVBLCount++;
306: if (nRunVBLs && nVBLCount >= nRunVBLs)
307: {
308: /* show VBLs/s */
309: Main_PauseEmulation(true);
310: exit(0);
311: }
1.1.1.13 root 312:
1.1.1.19 root 313: // FrameDuration_micro = (Sint64) ( 1000000.0 / nScreenRefreshRate + 0.5 ); /* round to closest integer */
314: FrameDuration_micro = ClocksTimings_GetVBLDuration_micro ( ConfigureParams.System.nMachineType , nScreenRefreshRate );
1.1.1.23 root 315: FrameDuration_micro *= nVBLSlowdown;
1.1.1.19 root 316: CurrentTicks = Time_GetTicks();
317:
1.1.1.23 root 318: if (DestTicks == 0) /* on first call, init DestTicks */
319: {
1.1.1.19 root 320: DestTicks = CurrentTicks + FrameDuration_micro;
1.1.1.23 root 321: }
1.1.1.19 root 322:
1.1.1.23 root 323: DestTicks += pulse_swallowing_count; /* audio.c - Audio_CallBack() */
1.1.1.21 root 324:
1.1.1.19 root 325: nDelay = DestTicks - CurrentTicks;
1.1.1.13 root 326:
1.1.1.15 root 327: /* Do not wait if we are in fast forward mode or if we are totally out of sync */
1.1.1.17 root 328: if (ConfigureParams.System.bFastForward == true
1.1.1.23 root 329: || nDelay < -4*FrameDuration_micro || nDelay > 50*FrameDuration_micro)
1.1.1.13 root 330: {
1.1.1.17 root 331: if (ConfigureParams.System.bFastForward == true)
332: {
333: if (!nFirstMilliTick)
334: nFirstMilliTick = Main_GetTicks();
335: }
1.1.1.15 root 336: if (nFrameSkips < ConfigureParams.Screen.nFrameSkips)
337: {
338: nFrameSkips += 1;
1.1.1.17 root 339: // Log_Printf(LOG_DEBUG, "Increased frameskip to %d\n", nFrameSkips);
1.1.1.15 root 340: }
1.1.1.19 root 341: /* Only update DestTicks for next VBL */
342: DestTicks = CurrentTicks + FrameDuration_micro;
1.1.1.13 root 343: return;
344: }
1.1.1.15 root 345: /* If automatic frameskip is enabled and delay's more than twice
346: * the effect of single frameskip, decrease frameskip
347: */
348: if (nFrameSkips > 0
349: && ConfigureParams.Screen.nFrameSkips >= AUTO_FRAMESKIP_LIMIT
1.1.1.19 root 350: && 2*nDelay > FrameDuration_micro/nFrameSkips)
1.1.1.15 root 351: {
352: nFrameSkips -= 1;
1.1.1.17 root 353: // Log_Printf(LOG_DEBUG, "Decreased frameskip to %d\n", nFrameSkips);
1.1.1.15 root 354: }
1.1.1.12 root 355:
1.1.1.13 root 356: if (bAccurateDelays)
357: {
358: /* Accurate sleeping is possible -> use SDL_Delay to free the CPU */
1.1.1.19 root 359: if (nDelay > 1000)
360: Time_Delay(nDelay - 1000);
1.1.1.13 root 361: }
362: else
363: {
364: /* No accurate SDL_Delay -> only wait if more than 5ms to go... */
1.1.1.19 root 365: if (nDelay > 5000)
366: Time_Delay(nDelay<10000 ? nDelay-1000 : 9000);
1.1.1.13 root 367: }
368:
369: /* Now busy-wait for the right tick: */
370: while (nDelay > 0)
371: {
1.1.1.19 root 372: CurrentTicks = Time_GetTicks();
373: nDelay = DestTicks - CurrentTicks;
1.1.1.23 root 374: /* If the delay is still bigger than one frame, somebody
375: * played tricks with the system clock and we have to abort */
376: if (nDelay > FrameDuration_micro)
377: break;
1.1.1.13 root 378: }
379:
1.1.1.19 root 380: //printf ( "tick %lld\n" , CurrentTicks );
381: /* Update DestTicks for next VBL */
382: DestTicks += FrameDuration_micro;
1.1.1.12 root 383: }
384:
385:
386: /*-----------------------------------------------------------------------*/
1.1.1.13 root 387: /**
388: * Since SDL_Delay and friends are very inaccurate on some systems, we have
389: * to check if we can rely on this delay function.
390: */
1.1.1.12 root 391: static void Main_CheckForAccurateDelays(void)
392: {
1.1.1.13 root 393: int nStartTicks, nEndTicks;
1.1.1.12 root 394:
1.1.1.13 root 395: /* Force a task switch now, so we have a longer timeslice afterwards */
396: SDL_Delay(10);
1.1.1.12 root 397:
1.1.1.13 root 398: nStartTicks = SDL_GetTicks();
399: SDL_Delay(1);
400: nEndTicks = SDL_GetTicks();
401:
402: /* If the delay took longer than 10ms, we are on an inaccurate system! */
403: bAccurateDelays = ((nEndTicks - nStartTicks) < 9);
404:
405: if (bAccurateDelays)
406: Log_Printf(LOG_DEBUG, "Host system has accurate delays. (%d)\n", nEndTicks - nStartTicks);
407: else
1.1.1.15 root 408: Log_Printf(LOG_WARN, "Host system does not have accurate delays. (%d)\n", nEndTicks - nStartTicks);
1.1.1.12 root 409: }
410:
411:
1.1 root 412: /* ----------------------------------------------------------------------- */
1.1.1.13 root 413: /**
1.1.1.24! root 414: * Set mouse pointer to new x,y coordinates and set flag to ignore
! 415: * the mouse event that is generated by SDL_WarpMouse().
! 416: *
! 417: * Skip the request is it's not position restore and mouse warping is disabled.
1.1.1.13 root 418: */
1.1.1.24! root 419: void Main_WarpMouse(int x, int y, bool restore)
1.1.1.9 root 420: {
1.1.1.24! root 421: if (!(restore || ConfigureParams.Screen.bMouseWarp))
! 422: return;
! 423: #if WITH_SDL2
! 424: SDL_WarpMouseInWindow(sdlWindow, x, y);
! 425: #else
! 426: SDL_WarpMouse(x, y);
! 427: #endif
! 428: bIgnoreNextMouseMotion = true;
1.1.1.9 root 429: }
430:
431:
432: /* ----------------------------------------------------------------------- */
1.1.1.13 root 433: /**
434: * Handle mouse motion event.
435: */
1.1.1.12 root 436: static void Main_HandleMouseMotion(SDL_Event *pEvent)
437: {
438: int dx, dy;
439: static int ax = 0, ay = 0;
440:
1.1.1.17 root 441: /* Ignore motion when position has changed right after a reset or TOS
442: * (especially version 4.04) might get confused and play key clicks */
443: if (bIgnoreNextMouseMotion || nVBLs < 10)
1.1.1.12 root 444: {
1.1.1.17 root 445: bIgnoreNextMouseMotion = false;
1.1.1.12 root 446: return;
447: }
448:
449: dx = pEvent->motion.xrel;
450: dy = pEvent->motion.yrel;
451:
1.1.1.13 root 452: /* In zoomed low res mode, we divide dx and dy by the zoom factor so that
453: * the ST mouse cursor stays in sync with the host mouse. However, we have
454: * to take care of lowest bit of dx and dy which will get lost when
455: * dividing. So we store these bits in ax and ay and add them to dx and dy
456: * the next time. */
457: if (nScreenZoomX != 1)
458: {
1.1.1.12 root 459: dx += ax;
1.1.1.13 root 460: ax = dx % nScreenZoomX;
461: dx /= nScreenZoomX;
1.1.1.12 root 462: }
1.1.1.13 root 463: if (nScreenZoomY != 1)
1.1.1.12 root 464: {
465: dy += ay;
1.1.1.13 root 466: ay = dy % nScreenZoomY;
467: dy /= nScreenZoomY;
1.1.1.12 root 468: }
469:
470: KeyboardProcessor.Mouse.dx += dx;
471: KeyboardProcessor.Mouse.dy += dy;
472: }
473:
474:
475: /* ----------------------------------------------------------------------- */
1.1.1.13 root 476: /**
477: * SDL message handler.
478: * Here we process the SDL events (keyboard, mouse, ...) and map it to
479: * Atari IKBD events.
480: */
1.1.1.8 root 481: void Main_EventHandler(void)
1.1 root 482: {
1.1.1.17 root 483: bool bContinueProcessing;
1.1.1.13 root 484: SDL_Event event;
1.1.1.17 root 485: int events;
486: int remotepause;
487:
1.1.1.15 root 488: do
1.1.1.13 root 489: {
1.1.1.17 root 490: bContinueProcessing = false;
491:
1.1.1.15 root 492: /* check remote process control */
1.1.1.17 root 493: remotepause = Control_CheckUpdates();
1.1.1.15 root 494:
495: if ( bEmulationActive || remotepause )
496: {
1.1.1.17 root 497: events = SDL_PollEvent(&event);
1.1.1.15 root 498: }
499: else
500: {
501: ShortCut_ActKey();
502: /* last (shortcut) event activated emulation? */
503: if ( bEmulationActive )
504: break;
1.1.1.17 root 505: events = SDL_WaitEvent(&event);
1.1.1.15 root 506: }
1.1.1.17 root 507: if (!events)
1.1.1.15 root 508: {
1.1.1.17 root 509: /* no events -> if emulation is active or
510: * user is quitting -> return from function.
511: */
1.1.1.15 root 512: continue;
513: }
1.1.1.13 root 514: switch (event.type)
515: {
1.1 root 516:
1.1.1.13 root 517: case SDL_QUIT:
1.1.1.23 root 518: Main_RequestQuit(0);
1.1.1.13 root 519: break;
1.1.1.17 root 520:
1.1.1.13 root 521: case SDL_MOUSEMOTION: /* Read/Update internal mouse position */
522: Main_HandleMouseMotion(&event);
1.1.1.17 root 523: bContinueProcessing = true;
1.1.1.13 root 524: break;
525:
526: case SDL_MOUSEBUTTONDOWN:
527: if (event.button.button == SDL_BUTTON_LEFT)
528: {
529: if (Keyboard.LButtonDblClk == 0)
530: Keyboard.bLButtonDown |= BUTTON_MOUSE; /* Set button down flag */
531: }
532: else if (event.button.button == SDL_BUTTON_RIGHT)
533: {
534: Keyboard.bRButtonDown |= BUTTON_MOUSE;
535: }
536: else if (event.button.button == SDL_BUTTON_MIDDLE)
537: {
538: /* Start double-click sequence in emulation time */
539: Keyboard.LButtonDblClk = 1;
540: }
1.1.1.24! root 541: #if !WITH_SDL2
1.1.1.13 root 542: else if (event.button.button == SDL_BUTTON_WHEELDOWN)
543: {
544: /* Simulate pressing the "cursor down" key */
1.1.1.17 root 545: IKBD_PressSTKey(0x50, true);
1.1.1.13 root 546: }
547: else if (event.button.button == SDL_BUTTON_WHEELUP)
548: {
549: /* Simulate pressing the "cursor up" key */
1.1.1.17 root 550: IKBD_PressSTKey(0x48, true);
1.1.1.13 root 551: }
1.1.1.24! root 552: #endif
1.1.1.13 root 553: break;
554:
555: case SDL_MOUSEBUTTONUP:
556: if (event.button.button == SDL_BUTTON_LEFT)
557: {
558: Keyboard.bLButtonDown &= ~BUTTON_MOUSE;
559: }
560: else if (event.button.button == SDL_BUTTON_RIGHT)
561: {
562: Keyboard.bRButtonDown &= ~BUTTON_MOUSE;
563: }
1.1.1.24! root 564: #if !WITH_SDL2
1.1.1.13 root 565: else if (event.button.button == SDL_BUTTON_WHEELDOWN)
566: {
567: /* Simulate releasing the "cursor down" key */
1.1.1.17 root 568: IKBD_PressSTKey(0x50, false);
1.1.1.13 root 569: }
570: else if (event.button.button == SDL_BUTTON_WHEELUP)
571: {
572: /* Simulate releasing the "cursor up" key */
1.1.1.17 root 573: IKBD_PressSTKey(0x48, false);
1.1.1.13 root 574: }
1.1.1.24! root 575: #endif
1.1.1.13 root 576: break;
577:
578: case SDL_KEYDOWN:
579: Keymap_KeyDown(&event.key.keysym);
580: break;
581:
582: case SDL_KEYUP:
583: Keymap_KeyUp(&event.key.keysym);
584: break;
1.1.1.17 root 585:
586: default:
587: /* don't let unknown events delay event processing */
588: bContinueProcessing = true;
589: break;
1.1.1.13 root 590: }
1.1.1.17 root 591: } while (bContinueProcessing || !(bEmulationActive || bQuitProgram));
1.1.1.13 root 592: }
593:
594:
595: /*-----------------------------------------------------------------------*/
596: /**
1.1.1.19 root 597: * Set Hatari window title. Use NULL for default
598: */
599: void Main_SetTitle(const char *title)
600: {
1.1.1.24! root 601: #if WITH_SDL2
! 602: if (title)
! 603: SDL_SetWindowTitle(sdlWindow, title);
! 604: else
! 605: SDL_SetWindowTitle(sdlWindow, PROG_NAME);
! 606: #else
1.1.1.19 root 607: if (title)
608: SDL_WM_SetCaption(title, "Hatari");
609: else
610: SDL_WM_SetCaption(PROG_NAME, "Hatari");
1.1.1.24! root 611: #endif
1.1.1.19 root 612: }
613:
614: /*-----------------------------------------------------------------------*/
615: /**
1.1.1.23 root 616: * Initialise emulation for some hardware components
1.1.1.24! root 617: * It is required to init those parts before parsing the parameters
! 618: * (for example, we should init FDC before inserting a disk and we
! 619: * need to know valid joysticks before selecting default joystick IDs)
1.1.1.23 root 620: */
621: static void Main_Init_HW(void)
622: {
1.1.1.24! root 623: Joy_Init();
1.1.1.23 root 624: FDC_Init();
1.1.1.24! root 625: STX_Init();
1.1.1.23 root 626: }
627:
628: /*-----------------------------------------------------------------------*/
629: /**
1.1.1.13 root 630: * Initialise emulation
631: */
1.1.1.8 root 632: static void Main_Init(void)
1.1 root 633: {
1.1.1.13 root 634: /* Open debug log file */
1.1.1.15 root 635: if (!Log_Init())
636: {
637: fprintf(stderr, "Logging/tracing initialization failed\n");
638: exit(-1);
639: }
1.1.1.13 root 640: Log_Printf(LOG_INFO, PROG_NAME ", compiled on: " __DATE__ ", " __TIME__ "\n");
641:
1.1.1.24! root 642: /* Init SDL's video subsystem. Note: Audio subsystem
! 643: will be initialized later (failure not fatal). */
1.1.1.18 root 644: if (SDL_Init(SDL_INIT_VIDEO | Opt_GetNoParachuteFlag()) < 0)
1.1.1.13 root 645: {
646: fprintf(stderr, "Could not initialize the SDL library:\n %s\n", SDL_GetError() );
647: exit(-1);
648: }
1.1.1.23 root 649:
650: if ( IPF_Init() != true )
651: {
652: fprintf(stderr, "Could not initialize the IPF support\n" );
653: exit(-1);
654: }
655:
1.1.1.19 root 656: ClocksTimings_InitMachine ( ConfigureParams.System.nMachineType );
657: Resolution_Init();
1.1.1.13 root 658: SDLGui_Init();
659: Printer_Init();
660: RS232_Init();
661: Midi_Init();
1.1.1.20 root 662: Control_CheckUpdates(); /* enable window embedding? */
1.1.1.13 root 663: Screen_Init();
1.1.1.19 root 664: Main_SetTitle(NULL);
1.1.1.13 root 665: HostScreen_Init();
1.1.1.22 root 666:
667: ACIA_Init( ACIA_Array , MachineClocks.ACIA_Freq , MachineClocks.ACIA_Freq );
668: IKBD_Init(); /* After ACIA_Init */
669:
1.1.1.18 root 670: DSP_Init();
1.1.1.13 root 671: Floppy_Init();
1.1.1.18 root 672: M68000_Init(); /* Init CPU emulation */
1.1.1.13 root 673: Audio_Init();
674: Keymap_Init();
675:
676: /* Init HD emulation */
1.1.1.18 root 677: HDC_Init();
1.1.1.16 root 678: Ide_Init();
1.1.1.13 root 679: GemDOS_Init();
680: if (ConfigureParams.HardDisk.bUseHardDiskDirectories)
681: {
1.1.1.18 root 682: /* uses variables set by HDC_Init()! */
1.1.1.13 root 683: GemDOS_InitDrives();
684: }
685:
686: if (Reset_Cold()) /* Reset all systems, load TOS image */
687: {
688: /* If loading of the TOS failed, we bring up the GUI to let the
689: * user choose another TOS ROM file. */
690: Dialog_DoProperty();
691: }
692: if (!bTosImageLoaded || bQuitProgram)
693: {
694: fprintf(stderr, "Failed to load TOS image!\n");
695: SDL_Quit();
696: exit(-2);
697: }
698:
699: IoMem_Init();
700: NvRam_Init();
701: Sound_Init();
1.1.1.18 root 702:
703: /* done as last, needs CPU & DSP running... */
704: DebugUI_Init();
1.1 root 705: }
706:
1.1.1.6 root 707:
1.1.1.2 root 708: /*-----------------------------------------------------------------------*/
1.1.1.13 root 709: /**
710: * Un-Initialise emulation
711: */
1.1.1.8 root 712: static void Main_UnInit(void)
1.1 root 713: {
1.1.1.13 root 714: Screen_ReturnFromFullScreen();
715: Floppy_UnInit();
716: HDC_UnInit();
717: Midi_UnInit();
718: RS232_UnInit();
719: Printer_UnInit();
720: IoMem_UnInit();
721: NvRam_UnInit();
722: GemDOS_UnInitDrives();
1.1.1.16 root 723: Ide_UnInit();
1.1.1.13 root 724: Joy_UnInit();
725: if (Sound_AreWeRecording())
726: Sound_EndRecording();
727: Audio_UnInit();
728: SDLGui_UnInit();
1.1.1.18 root 729: DSP_UnInit();
1.1.1.15 root 730: HostScreen_UnInit();
1.1.1.13 root 731: Screen_UnInit();
732: Exit680x0();
1.1 root 733:
1.1.1.23 root 734: IPF_Exit();
735:
1.1.1.13 root 736: /* SDL uninit: */
737: SDL_Quit();
1.1.1.10 root 738:
1.1.1.13 root 739: /* Close debug log file */
740: Log_UnInit();
1.1 root 741: }
742:
1.1.1.6 root 743:
1.1.1.2 root 744: /*-----------------------------------------------------------------------*/
1.1.1.13 root 745: /**
1.1.1.15 root 746: * Load initial configuration file(s)
747: */
748: static void Main_LoadInitialConfig(void)
749: {
750: char *psGlobalConfig;
751:
752: psGlobalConfig = malloc(FILENAME_MAX);
753: if (psGlobalConfig)
754: {
755: #if defined(__AMIGAOS4__)
756: strncpy(psGlobalConfig, CONFDIR"hatari.cfg", FILENAME_MAX);
757: #else
758: snprintf(psGlobalConfig, FILENAME_MAX, CONFDIR"%chatari.cfg", PATHSEP);
759: #endif
760: /* Try to load the global configuration file */
761: Configuration_Load(psGlobalConfig);
762:
763: free(psGlobalConfig);
764: }
765:
766: /* Now try the users configuration file */
767: Configuration_Load(NULL);
768: }
769:
770: /*-----------------------------------------------------------------------*/
771: /**
772: * Set TOS etc information and initial help message
773: */
774: static void Main_StatusbarSetup(void)
775: {
776: const char *name = NULL;
777: SDLKey key;
778:
779: key = ConfigureParams.Shortcut.withoutModifier[SHORTCUT_OPTIONS];
780: if (!key)
781: key = ConfigureParams.Shortcut.withModifier[SHORTCUT_OPTIONS];
782: if (key)
783: name = SDL_GetKeyName(key);
784: if (name)
785: {
786: char message[24], *keyname;
1.1.1.18 root 787: #ifdef _MUDFLAP
1.1.1.21 root 788: __mf_register((void*)name, 32, __MF_TYPE_GUESS, "SDL keyname");
1.1.1.18 root 789: #endif
1.1.1.15 root 790: keyname = Str_ToUpper(strdup(name));
791: snprintf(message, sizeof(message), "Press %s for Options", keyname);
792: free(keyname);
793:
1.1.1.23 root 794: Statusbar_AddMessage(message, 5000);
1.1.1.15 root 795: }
796: /* update information loaded by Main_Init() */
797: Statusbar_UpdateInfo();
798: }
799:
1.1.1.24! root 800:
1.1.1.15 root 801: /**
1.1.1.13 root 802: * Main
1.1.1.15 root 803: *
804: * Note: 'argv' cannot be declared const, MinGW would then fail to link.
1.1.1.13 root 805: */
1.1 root 806: int main(int argc, char *argv[])
807: {
1.1.1.13 root 808: /* Generate random seed */
809: srand(time(NULL));
810:
1.1.1.24! root 811: /* Logs default to stderr at start */
! 812: Log_Default();
! 813:
1.1.1.13 root 814: /* Initialize directory strings */
815: Paths_Init(argv[0]);
816:
1.1.1.23 root 817: /* Init some HW components before parsing the configuration / parameters */
818: Main_Init_HW();
819:
820: /* Set default configuration values */
1.1.1.13 root 821: Configuration_SetDefault();
1.1.1.8 root 822:
1.1.1.13 root 823: /* Now load the values from the configuration file */
1.1.1.15 root 824: Main_LoadInitialConfig();
1.1 root 825:
1.1.1.15 root 826: /* Check for any passed parameters */
1.1.1.19 root 827: if (!Opt_ParseParameters(argc, (const char * const *)argv))
1.1.1.15 root 828: {
829: return 1;
830: }
1.1.1.17 root 831: /* monitor type option might require "reset" -> true */
832: Configuration_Apply(true);
1.1.1.2 root 833:
1.1.1.13 root 834: #ifdef WIN32
835: Win_OpenCon();
836: #endif
1.1.1.7 root 837:
1.1.1.13 root 838: #if HAVE_SETENV
1.1.1.20 root 839: /* Needed on maemo but useful also with normal X11 window managers for
840: * window grouping when you have multiple Hatari SDL windows open */
1.1.1.13 root 841: setenv("SDL_VIDEO_X11_WMCLASS", "hatari", 1);
1.1.1.20 root 842:
843: /* Needed for proper behavior of Caps Lock on some systems */
844: setenv("SDL_DISABLE_LOCK_KEYS", "1", 1);
1.1.1.13 root 845: #endif
1.1 root 846:
1.1.1.13 root 847: /* Init emulator system */
848: Main_Init();
1.1 root 849:
1.1.1.15 root 850: /* Set initial Statusbar information */
851: Main_StatusbarSetup();
852:
1.1.1.13 root 853: /* Check if SDL_Delay is accurate */
854: Main_CheckForAccurateDelays();
1.1.1.12 root 855:
1.1.1.22 root 856: if ( AviRecordOnStartup ) /* Immediately starts avi recording ? */
1.1.1.19 root 857: Avi_StartRecording ( ConfigureParams.Video.AviRecordFile , ConfigureParams.Screen.bCrop ,
858: ConfigureParams.Video.AviRecordFps == 0 ?
859: ClocksTimings_GetVBLPerSec ( ConfigureParams.System.nMachineType , nScreenRefreshRate ) :
860: (Uint32)ConfigureParams.Video.AviRecordFps << CLOCKS_TIMINGS_SHIFT_VBL ,
861: 1 << CLOCKS_TIMINGS_SHIFT_VBL ,
862: ConfigureParams.Video.AviRecordVcodec );
1.1.1.18 root 863:
1.1.1.13 root 864: /* Run emulation */
865: Main_UnPauseEmulation();
866: M68000_Start(); /* Start emulation */
1.1 root 867:
1.1.1.18 root 868: if (bRecordingAvi)
869: {
870: /* cleanly close the avi file */
871: Statusbar_AddMessage("Finishing AVI file...", 100);
1.1.1.23 root 872: Statusbar_Update(sdlscrn, true);
1.1.1.18 root 873: Avi_StopRecording();
874: }
1.1.1.13 root 875: /* Un-init emulation system */
876: Main_UnInit();
1.1 root 877:
1.1.1.23 root 878: return nQuitValue;
1.1 root 879: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.