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