|
|
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"
1.1.1.27! root 44: #include "scc.h"
1.1 root 45: #include "screen.h"
1.1.1.5 root 46: #include "sdlgui.h"
1.1 root 47: #include "shortcut.h"
48: #include "sound.h"
1.1.1.18 root 49: #include "dmaSnd.h"
1.1.1.15 root 50: #include "statusbar.h"
1.1.1.8 root 51: #include "stMemory.h"
1.1.1.15 root 52: #include "str.h"
1.1 root 53: #include "tos.h"
54: #include "video.h"
1.1.1.18 root 55: #include "avi_record.h"
56: #include "debugui.h"
1.1.1.19 root 57: #include "clocks_timings.h"
1.1 root 58:
1.1.1.13 root 59: #include "hatari-glue.h"
60:
61: #include "falcon/dsp.h"
1.1.1.25 root 62: #include "falcon/videl.h"
1.1 root 63:
1.1.1.19 root 64: #if HAVE_GETTIMEOFDAY
65: #include <sys/time.h>
66: #endif
67:
1.1.1.23 root 68: #ifdef WIN32
69: #include "gui-win/opencon.h"
70: #endif
1.1 root 71:
1.1.1.17 root 72: bool bQuitProgram = false; /* Flag to quit program cleanly */
1.1.1.23 root 73: static int nQuitValue; /* exit value */
1.1.1.13 root 74:
1.1.1.19 root 75: static Uint32 nRunVBLs; /* Whether and how many VBLS to run before exit */
1.1.1.17 root 76: static Uint32 nFirstMilliTick; /* Ticks when VBL counting started */
77: static Uint32 nVBLCount; /* Frame count */
1.1.1.23 root 78: static int nVBLSlowdown = 1; /* host VBL wait multiplier */
1.1.1.17 root 79:
80: static bool bEmulationActive = true; /* Run emulation when started */
1.1.1.15 root 81: static bool bAccurateDelays; /* Host system has an accurate SDL_Delay()? */
1.1.1.17 root 82: static bool bIgnoreNextMouseMotion = false; /* Next mouse motion will be ignored (needed after SDL_WarpMouse) */
1.1 root 83:
84: /*-----------------------------------------------------------------------*/
1.1.1.13 root 85: /**
1.1.1.17 root 86: * Return current time as millisecond for performance measurements.
87: *
88: * (On Unix only time spent by Hatari itself is counted, on other
89: * platforms less accurate SDL "wall clock".)
90: */
91: #if HAVE_SYS_TIMES_H
92: #include <unistd.h>
93: #include <sys/times.h>
94: static Uint32 Main_GetTicks(void)
95: {
96: static unsigned int ticks_to_msec = 0;
97: struct tms fields;
98: if (!ticks_to_msec)
99: {
100: ticks_to_msec = sysconf(_SC_CLK_TCK);
1.1.1.27! root 101: Log_Printf(LOG_INFO, "OS clock ticks / second: %d\n", ticks_to_msec);
1.1.1.17 root 102: /* Linux has 100Hz virtual clock so no accuracy loss there */
103: ticks_to_msec = 1000UL / ticks_to_msec;
104: }
105: /* return milliseconds (clock ticks) spent in this process
106: */
107: times(&fields);
108: return ticks_to_msec * fields.tms_utime;
109: }
110: #else
1.1.1.18 root 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;
1.1.1.27! root 190: Log_Printf(LOG_INFO, "SPEED: %.1f VBL/s (%d/%.1fs), diff=%.1f%%\n",
1.1.1.17 root 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.27! root 261: /**
! 262: * Set exit value and enable quit flag
! 263: */
! 264: void Main_SetQuitValue(int exitval)
! 265: {
! 266: bQuitProgram = true;
! 267: M68000_SetSpecial(SPCFLAG_BRK);
! 268: nQuitValue = exitval;
! 269: }
! 270:
1.1.1.12 root 271: /*-----------------------------------------------------------------------*/
1.1.1.13 root 272: /**
1.1.1.19 root 273: * Set how many VBLs Hatari should run, from the moment this function
1.1.1.27! root 274: * is called and return zero.
! 275: *
! 276: * If zero value given instead, returns earlier set VBL count.
1.1.1.19 root 277: */
1.1.1.27! root 278: Uint32 Main_SetRunVBLs(Uint32 vbls)
1.1.1.19 root 279: {
1.1.1.27! root 280: if (!vbls)
! 281: return nRunVBLs;
! 282:
1.1.1.19 root 283: nRunVBLs = vbls;
284: nVBLCount = 0;
1.1.1.27! root 285: return 0;
1.1.1.19 root 286: }
287:
288: /*-----------------------------------------------------------------------*/
289: /**
1.1.1.23 root 290: * Set VBL wait slowdown factor/multiplayer
1.1.1.27! root 291: *
! 292: * Return NULL on success, error string on error
1.1.1.23 root 293: */
1.1.1.27! root 294: const char* Main_SetVBLSlowdown(int factor)
1.1.1.23 root 295: {
1.1.1.27! root 296: if (factor < 1 || factor > 30) {
! 297: return "invalid VBL slowdown factor, should be 1-30";
1.1.1.23 root 298: }
299: nVBLSlowdown = factor;
1.1.1.27! root 300: return NULL;
1.1.1.23 root 301: }
302:
303: /*-----------------------------------------------------------------------*/
304: /**
1.1.1.13 root 305: * This function waits on each emulated VBL to synchronize the real time
306: * with the emulated ST.
307: * Unfortunately SDL_Delay and other sleep functions like usleep or nanosleep
1.1.1.26 root 308: * are very inaccurate on some systems like Linux 2.4 or macOS (they can only
309: * wait for a multiple of 10ms due to the scheduler on these systems), so we
310: * have to "busy wait" there to get an accurate timing.
1.1.1.19 root 311: * All times are expressed as micro seconds, to avoid too much rounding error.
1.1.1.13 root 312: */
1.1.1.12 root 313: void Main_WaitOnVbl(void)
314: {
1.1.1.19 root 315: Sint64 CurrentTicks;
316: static Sint64 DestTicks = 0;
317: Sint64 FrameDuration_micro;
318: Sint64 nDelay;
1.1.1.13 root 319:
1.1.1.18 root 320: nVBLCount++;
321: if (nRunVBLs && nVBLCount >= nRunVBLs)
322: {
323: /* show VBLs/s */
324: Main_PauseEmulation(true);
325: exit(0);
326: }
1.1.1.13 root 327:
1.1.1.19 root 328: // FrameDuration_micro = (Sint64) ( 1000000.0 / nScreenRefreshRate + 0.5 ); /* round to closest integer */
329: FrameDuration_micro = ClocksTimings_GetVBLDuration_micro ( ConfigureParams.System.nMachineType , nScreenRefreshRate );
1.1.1.23 root 330: FrameDuration_micro *= nVBLSlowdown;
1.1.1.19 root 331: CurrentTicks = Time_GetTicks();
332:
1.1.1.23 root 333: if (DestTicks == 0) /* on first call, init DestTicks */
334: {
1.1.1.19 root 335: DestTicks = CurrentTicks + FrameDuration_micro;
1.1.1.23 root 336: }
1.1.1.19 root 337:
1.1.1.23 root 338: DestTicks += pulse_swallowing_count; /* audio.c - Audio_CallBack() */
1.1.1.21 root 339:
1.1.1.19 root 340: nDelay = DestTicks - CurrentTicks;
1.1.1.13 root 341:
1.1.1.15 root 342: /* Do not wait if we are in fast forward mode or if we are totally out of sync */
1.1.1.26 root 343: /* or if we are in benchmark mode */
1.1.1.17 root 344: if (ConfigureParams.System.bFastForward == true
1.1.1.26 root 345: || nDelay < -4*FrameDuration_micro || nDelay > 50*FrameDuration_micro
346: || BenchmarkMode )
347:
1.1.1.13 root 348: {
1.1.1.26 root 349: if ( ( ConfigureParams.System.bFastForward == true )
350: || ( BenchmarkMode == true ) )
1.1.1.17 root 351: {
352: if (!nFirstMilliTick)
353: nFirstMilliTick = Main_GetTicks();
354: }
1.1.1.15 root 355: if (nFrameSkips < ConfigureParams.Screen.nFrameSkips)
356: {
357: nFrameSkips += 1;
1.1.1.17 root 358: // Log_Printf(LOG_DEBUG, "Increased frameskip to %d\n", nFrameSkips);
1.1.1.15 root 359: }
1.1.1.19 root 360: /* Only update DestTicks for next VBL */
361: DestTicks = CurrentTicks + FrameDuration_micro;
1.1.1.13 root 362: return;
363: }
1.1.1.26 root 364:
1.1.1.15 root 365: /* If automatic frameskip is enabled and delay's more than twice
366: * the effect of single frameskip, decrease frameskip
367: */
368: if (nFrameSkips > 0
369: && ConfigureParams.Screen.nFrameSkips >= AUTO_FRAMESKIP_LIMIT
1.1.1.19 root 370: && 2*nDelay > FrameDuration_micro/nFrameSkips)
1.1.1.15 root 371: {
372: nFrameSkips -= 1;
1.1.1.17 root 373: // Log_Printf(LOG_DEBUG, "Decreased frameskip to %d\n", nFrameSkips);
1.1.1.15 root 374: }
1.1.1.12 root 375:
1.1.1.13 root 376: if (bAccurateDelays)
377: {
378: /* Accurate sleeping is possible -> use SDL_Delay to free the CPU */
1.1.1.19 root 379: if (nDelay > 1000)
380: Time_Delay(nDelay - 1000);
1.1.1.13 root 381: }
382: else
383: {
384: /* No accurate SDL_Delay -> only wait if more than 5ms to go... */
1.1.1.19 root 385: if (nDelay > 5000)
386: Time_Delay(nDelay<10000 ? nDelay-1000 : 9000);
1.1.1.13 root 387: }
388:
389: /* Now busy-wait for the right tick: */
390: while (nDelay > 0)
391: {
1.1.1.19 root 392: CurrentTicks = Time_GetTicks();
393: nDelay = DestTicks - CurrentTicks;
1.1.1.23 root 394: /* If the delay is still bigger than one frame, somebody
395: * played tricks with the system clock and we have to abort */
396: if (nDelay > FrameDuration_micro)
397: break;
1.1.1.13 root 398: }
399:
1.1.1.19 root 400: //printf ( "tick %lld\n" , CurrentTicks );
401: /* Update DestTicks for next VBL */
402: DestTicks += FrameDuration_micro;
1.1.1.12 root 403: }
404:
405:
406: /*-----------------------------------------------------------------------*/
1.1.1.13 root 407: /**
408: * Since SDL_Delay and friends are very inaccurate on some systems, we have
409: * to check if we can rely on this delay function.
410: */
1.1.1.12 root 411: static void Main_CheckForAccurateDelays(void)
412: {
1.1.1.13 root 413: int nStartTicks, nEndTicks;
1.1.1.12 root 414:
1.1.1.13 root 415: /* Force a task switch now, so we have a longer timeslice afterwards */
416: SDL_Delay(10);
1.1.1.12 root 417:
1.1.1.13 root 418: nStartTicks = SDL_GetTicks();
419: SDL_Delay(1);
420: nEndTicks = SDL_GetTicks();
421:
422: /* If the delay took longer than 10ms, we are on an inaccurate system! */
423: bAccurateDelays = ((nEndTicks - nStartTicks) < 9);
424:
425: if (bAccurateDelays)
426: Log_Printf(LOG_DEBUG, "Host system has accurate delays. (%d)\n", nEndTicks - nStartTicks);
427: else
1.1.1.15 root 428: Log_Printf(LOG_WARN, "Host system does not have accurate delays. (%d)\n", nEndTicks - nStartTicks);
1.1.1.12 root 429: }
430:
431:
1.1 root 432: /* ----------------------------------------------------------------------- */
1.1.1.13 root 433: /**
1.1.1.24 root 434: * Set mouse pointer to new x,y coordinates and set flag to ignore
435: * the mouse event that is generated by SDL_WarpMouse().
436: *
437: * Skip the request is it's not position restore and mouse warping is disabled.
1.1.1.13 root 438: */
1.1.1.24 root 439: void Main_WarpMouse(int x, int y, bool restore)
1.1.1.9 root 440: {
1.1.1.24 root 441: if (!(restore || ConfigureParams.Screen.bMouseWarp))
442: return;
443: #if WITH_SDL2
444: SDL_WarpMouseInWindow(sdlWindow, x, y);
445: #else
446: SDL_WarpMouse(x, y);
447: #endif
448: bIgnoreNextMouseMotion = true;
1.1.1.9 root 449: }
450:
451:
452: /* ----------------------------------------------------------------------- */
1.1.1.13 root 453: /**
454: * Handle mouse motion event.
455: */
1.1.1.12 root 456: static void Main_HandleMouseMotion(SDL_Event *pEvent)
457: {
458: int dx, dy;
459: static int ax = 0, ay = 0;
460:
1.1.1.17 root 461: /* Ignore motion when position has changed right after a reset or TOS
462: * (especially version 4.04) might get confused and play key clicks */
463: if (bIgnoreNextMouseMotion || nVBLs < 10)
1.1.1.12 root 464: {
1.1.1.17 root 465: bIgnoreNextMouseMotion = false;
1.1.1.12 root 466: return;
467: }
468:
469: dx = pEvent->motion.xrel;
470: dy = pEvent->motion.yrel;
471:
1.1.1.13 root 472: /* In zoomed low res mode, we divide dx and dy by the zoom factor so that
473: * the ST mouse cursor stays in sync with the host mouse. However, we have
474: * to take care of lowest bit of dx and dy which will get lost when
475: * dividing. So we store these bits in ax and ay and add them to dx and dy
476: * the next time. */
477: if (nScreenZoomX != 1)
478: {
1.1.1.12 root 479: dx += ax;
1.1.1.13 root 480: ax = dx % nScreenZoomX;
481: dx /= nScreenZoomX;
1.1.1.12 root 482: }
1.1.1.13 root 483: if (nScreenZoomY != 1)
1.1.1.12 root 484: {
485: dy += ay;
1.1.1.13 root 486: ay = dy % nScreenZoomY;
487: dy /= nScreenZoomY;
1.1.1.12 root 488: }
489:
490: KeyboardProcessor.Mouse.dx += dx;
491: KeyboardProcessor.Mouse.dy += dy;
492: }
493:
494:
495: /* ----------------------------------------------------------------------- */
1.1.1.13 root 496: /**
497: * SDL message handler.
498: * Here we process the SDL events (keyboard, mouse, ...) and map it to
499: * Atari IKBD events.
500: */
1.1.1.8 root 501: void Main_EventHandler(void)
1.1 root 502: {
1.1.1.17 root 503: bool bContinueProcessing;
1.1.1.13 root 504: SDL_Event event;
1.1.1.17 root 505: int events;
506: int remotepause;
507:
1.1.1.15 root 508: do
1.1.1.13 root 509: {
1.1.1.17 root 510: bContinueProcessing = false;
511:
1.1.1.15 root 512: /* check remote process control */
1.1.1.17 root 513: remotepause = Control_CheckUpdates();
1.1.1.15 root 514:
515: if ( bEmulationActive || remotepause )
516: {
1.1.1.17 root 517: events = SDL_PollEvent(&event);
1.1.1.15 root 518: }
519: else
520: {
521: ShortCut_ActKey();
522: /* last (shortcut) event activated emulation? */
523: if ( bEmulationActive )
524: break;
1.1.1.17 root 525: events = SDL_WaitEvent(&event);
1.1.1.15 root 526: }
1.1.1.17 root 527: if (!events)
1.1.1.15 root 528: {
1.1.1.17 root 529: /* no events -> if emulation is active or
530: * user is quitting -> return from function.
531: */
1.1.1.15 root 532: continue;
533: }
1.1.1.13 root 534: switch (event.type)
535: {
536: case SDL_QUIT:
1.1.1.23 root 537: Main_RequestQuit(0);
1.1.1.13 root 538: break;
1.1.1.25 root 539:
540: case SDL_KEYDOWN:
1.1.1.26 root 541: #if WITH_SDL2
542: if (event.key.repeat) {
543: bContinueProcessing = true;
544: break;
545: }
546: #endif
1.1.1.25 root 547: Keymap_KeyDown(&event.key.keysym);
548: break;
549:
550: case SDL_KEYUP:
551: Keymap_KeyUp(&event.key.keysym);
552: break;
553:
1.1.1.13 root 554: case SDL_MOUSEMOTION: /* Read/Update internal mouse position */
555: Main_HandleMouseMotion(&event);
1.1.1.17 root 556: bContinueProcessing = true;
1.1.1.13 root 557: break;
558:
559: case SDL_MOUSEBUTTONDOWN:
560: if (event.button.button == SDL_BUTTON_LEFT)
561: {
562: if (Keyboard.LButtonDblClk == 0)
563: Keyboard.bLButtonDown |= BUTTON_MOUSE; /* Set button down flag */
564: }
565: else if (event.button.button == SDL_BUTTON_RIGHT)
566: {
567: Keyboard.bRButtonDown |= BUTTON_MOUSE;
568: }
569: else if (event.button.button == SDL_BUTTON_MIDDLE)
570: {
571: /* Start double-click sequence in emulation time */
572: Keyboard.LButtonDblClk = 1;
573: }
1.1.1.24 root 574: #if !WITH_SDL2
1.1.1.13 root 575: else if (event.button.button == SDL_BUTTON_WHEELDOWN)
576: {
577: /* Simulate pressing the "cursor down" key */
1.1.1.17 root 578: IKBD_PressSTKey(0x50, true);
1.1.1.13 root 579: }
580: else if (event.button.button == SDL_BUTTON_WHEELUP)
581: {
582: /* Simulate pressing the "cursor up" key */
1.1.1.17 root 583: IKBD_PressSTKey(0x48, true);
1.1.1.13 root 584: }
1.1.1.24 root 585: #endif
1.1.1.13 root 586: break;
587:
588: case SDL_MOUSEBUTTONUP:
589: if (event.button.button == SDL_BUTTON_LEFT)
590: {
591: Keyboard.bLButtonDown &= ~BUTTON_MOUSE;
592: }
593: else if (event.button.button == SDL_BUTTON_RIGHT)
594: {
595: Keyboard.bRButtonDown &= ~BUTTON_MOUSE;
596: }
1.1.1.24 root 597: #if !WITH_SDL2
1.1.1.13 root 598: else if (event.button.button == SDL_BUTTON_WHEELDOWN)
599: {
600: /* Simulate releasing the "cursor down" key */
1.1.1.17 root 601: IKBD_PressSTKey(0x50, false);
1.1.1.13 root 602: }
603: else if (event.button.button == SDL_BUTTON_WHEELUP)
604: {
605: /* Simulate releasing the "cursor up" key */
1.1.1.17 root 606: IKBD_PressSTKey(0x48, false);
1.1.1.13 root 607: }
1.1.1.24 root 608: #endif
1.1.1.13 root 609: break;
610:
1.1.1.25 root 611: #if WITH_SDL2
612: case SDL_MOUSEWHEEL:
613: /* Simulate cursor keys on mouse wheel events */
614: if (event.wheel.x > 0)
615: {
616: IKBD_PressSTKey(0x4d, true);
617: IKBD_PressSTKey(0x4d, false);
618: }
619: else if (event.wheel.x < 0)
620: {
621: IKBD_PressSTKey(0x4b, true);
622: IKBD_PressSTKey(0x4b, false);
623: }
624: if (event.wheel.y < 0)
625: {
626: IKBD_PressSTKey(0x50, true);
627: IKBD_PressSTKey(0x50, false);
628: }
629: else if (event.wheel.y > 0)
630: {
631: IKBD_PressSTKey(0x48, true);
632: IKBD_PressSTKey(0x48, false);
633: }
1.1.1.13 root 634: break;
635:
1.1.1.25 root 636: case SDL_WINDOWEVENT:
1.1.1.26 root 637: if (event.window.event == SDL_WINDOWEVENT_EXPOSED
638: && !ConfigureParams.Screen.bUseSdlRenderer)
639: {
640: /* Hack: Redraw screen here when going into
641: * fullscreen mode without SDL renderer */
642: sdlscrn = SDL_GetWindowSurface(sdlWindow);
643: Screen_SetFullUpdate();
644: Statusbar_Init(sdlscrn);
1.1.1.25 root 645: }
1.1.1.27! root 646: /* Note: any changes here should most likely be done
! 647: * also in sdlgui.c::SDLGui_DoDialog()
! 648: */
! 649: if (event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED
! 650: || event.window.event == SDL_WINDOWEVENT_RESTORED
! 651: || event.window.event == SDL_WINDOWEVENT_EXPOSED)
! 652: {
! 653: SDL_UpdateRect(sdlscrn, 0, 0, 0, 0);
! 654: }
1.1.1.25 root 655: bContinueProcessing = true;
1.1.1.13 root 656: break;
1.1.1.27! root 657: #endif /* WITH_SDL2 */
1.1.1.17 root 658:
1.1.1.25 root 659: default:
1.1.1.17 root 660: /* don't let unknown events delay event processing */
661: bContinueProcessing = true;
662: break;
1.1.1.13 root 663: }
1.1.1.17 root 664: } while (bContinueProcessing || !(bEmulationActive || bQuitProgram));
1.1.1.13 root 665: }
666:
667:
668: /*-----------------------------------------------------------------------*/
669: /**
1.1.1.19 root 670: * Set Hatari window title. Use NULL for default
671: */
672: void Main_SetTitle(const char *title)
673: {
1.1.1.24 root 674: #if WITH_SDL2
675: if (title)
676: SDL_SetWindowTitle(sdlWindow, title);
677: else
678: SDL_SetWindowTitle(sdlWindow, PROG_NAME);
679: #else
1.1.1.19 root 680: if (title)
681: SDL_WM_SetCaption(title, "Hatari");
682: else
683: SDL_WM_SetCaption(PROG_NAME, "Hatari");
1.1.1.24 root 684: #endif
1.1.1.19 root 685: }
686:
687: /*-----------------------------------------------------------------------*/
688: /**
1.1.1.23 root 689: * Initialise emulation for some hardware components
1.1.1.24 root 690: * It is required to init those parts before parsing the parameters
691: * (for example, we should init FDC before inserting a disk and we
692: * need to know valid joysticks before selecting default joystick IDs)
1.1.1.23 root 693: */
694: static void Main_Init_HW(void)
695: {
1.1.1.24 root 696: Joy_Init();
1.1.1.23 root 697: FDC_Init();
1.1.1.24 root 698: STX_Init();
1.1.1.25 root 699: Video_InitTimings();
1.1.1.23 root 700: }
701:
702: /*-----------------------------------------------------------------------*/
703: /**
1.1.1.13 root 704: * Initialise emulation
705: */
1.1.1.8 root 706: static void Main_Init(void)
1.1 root 707: {
1.1.1.13 root 708: /* Open debug log file */
1.1.1.15 root 709: if (!Log_Init())
710: {
1.1.1.27! root 711: fprintf(stderr, "ERROR: logging/tracing initialization failed\n");
1.1.1.15 root 712: exit(-1);
713: }
1.1.1.13 root 714: Log_Printf(LOG_INFO, PROG_NAME ", compiled on: " __DATE__ ", " __TIME__ "\n");
715:
1.1.1.24 root 716: /* Init SDL's video subsystem. Note: Audio subsystem
717: will be initialized later (failure not fatal). */
1.1.1.18 root 718: if (SDL_Init(SDL_INIT_VIDEO | Opt_GetNoParachuteFlag()) < 0)
1.1.1.13 root 719: {
1.1.1.27! root 720: fprintf(stderr, "ERROR: could not initialize the SDL library:\n %s\n", SDL_GetError() );
1.1.1.13 root 721: exit(-1);
722: }
1.1.1.23 root 723:
724: if ( IPF_Init() != true )
725: {
1.1.1.27! root 726: fprintf(stderr, "ERROR: could not initialize the IPF support\n" );
1.1.1.23 root 727: exit(-1);
728: }
729:
1.1.1.19 root 730: ClocksTimings_InitMachine ( ConfigureParams.System.nMachineType );
1.1.1.25 root 731: Video_SetTimings ( ConfigureParams.System.nMachineType , ConfigureParams.System.VideoTimingMode );
732:
1.1.1.19 root 733: Resolution_Init();
1.1.1.13 root 734: SDLGui_Init();
735: Printer_Init();
736: RS232_Init();
1.1.1.27! root 737: SCC_Init();
1.1.1.13 root 738: Midi_Init();
1.1.1.20 root 739: Control_CheckUpdates(); /* enable window embedding? */
1.1.1.25 root 740: Videl_Init();
1.1.1.13 root 741: Screen_Init();
1.1.1.19 root 742: Main_SetTitle(NULL);
1.1.1.22 root 743:
1.1.1.26 root 744: STMemory_Init ( ConfigureParams.Memory.STRamSize_KB * 1024 );
745:
1.1.1.22 root 746: ACIA_Init( ACIA_Array , MachineClocks.ACIA_Freq , MachineClocks.ACIA_Freq );
747: IKBD_Init(); /* After ACIA_Init */
748:
1.1.1.18 root 749: DSP_Init();
1.1.1.13 root 750: Floppy_Init();
1.1.1.18 root 751: M68000_Init(); /* Init CPU emulation */
1.1.1.13 root 752: Audio_Init();
753: Keymap_Init();
754:
755: /* Init HD emulation */
1.1.1.18 root 756: HDC_Init();
1.1.1.16 root 757: Ide_Init();
1.1.1.13 root 758: GemDOS_Init();
759: if (ConfigureParams.HardDisk.bUseHardDiskDirectories)
760: {
1.1.1.18 root 761: /* uses variables set by HDC_Init()! */
1.1.1.13 root 762: GemDOS_InitDrives();
763: }
764:
765: if (Reset_Cold()) /* Reset all systems, load TOS image */
766: {
767: /* If loading of the TOS failed, we bring up the GUI to let the
768: * user choose another TOS ROM file. */
769: Dialog_DoProperty();
770: }
771: if (!bTosImageLoaded || bQuitProgram)
772: {
1.1.1.26 root 773: if (!bTosImageLoaded)
1.1.1.27! root 774: fprintf(stderr, "ERROR: failed to load TOS image!\n");
1.1.1.13 root 775: SDL_Quit();
776: exit(-2);
777: }
778:
779: IoMem_Init();
780: NvRam_Init();
781: Sound_Init();
1.1.1.18 root 782:
783: /* done as last, needs CPU & DSP running... */
784: DebugUI_Init();
1.1 root 785: }
786:
1.1.1.6 root 787:
1.1.1.2 root 788: /*-----------------------------------------------------------------------*/
1.1.1.13 root 789: /**
790: * Un-Initialise emulation
791: */
1.1.1.8 root 792: static void Main_UnInit(void)
1.1 root 793: {
1.1.1.13 root 794: Screen_ReturnFromFullScreen();
795: Floppy_UnInit();
796: HDC_UnInit();
797: Midi_UnInit();
1.1.1.27! root 798: SCC_UnInit();
1.1.1.13 root 799: RS232_UnInit();
800: Printer_UnInit();
801: IoMem_UnInit();
802: NvRam_UnInit();
803: GemDOS_UnInitDrives();
1.1.1.16 root 804: Ide_UnInit();
1.1.1.13 root 805: Joy_UnInit();
806: if (Sound_AreWeRecording())
807: Sound_EndRecording();
808: Audio_UnInit();
809: SDLGui_UnInit();
1.1.1.18 root 810: DSP_UnInit();
1.1.1.13 root 811: Screen_UnInit();
812: Exit680x0();
1.1 root 813:
1.1.1.23 root 814: IPF_Exit();
815:
1.1.1.13 root 816: /* SDL uninit: */
817: SDL_Quit();
1.1.1.10 root 818:
1.1.1.13 root 819: /* Close debug log file */
820: Log_UnInit();
1.1.1.27! root 821:
! 822: Paths_UnInit();
1.1 root 823: }
824:
1.1.1.6 root 825:
1.1.1.2 root 826: /*-----------------------------------------------------------------------*/
1.1.1.13 root 827: /**
1.1.1.15 root 828: * Load initial configuration file(s)
829: */
830: static void Main_LoadInitialConfig(void)
831: {
832: char *psGlobalConfig;
833:
834: psGlobalConfig = malloc(FILENAME_MAX);
835: if (psGlobalConfig)
836: {
837: #if defined(__AMIGAOS4__)
838: strncpy(psGlobalConfig, CONFDIR"hatari.cfg", FILENAME_MAX);
839: #else
840: snprintf(psGlobalConfig, FILENAME_MAX, CONFDIR"%chatari.cfg", PATHSEP);
841: #endif
842: /* Try to load the global configuration file */
843: Configuration_Load(psGlobalConfig);
844:
845: free(psGlobalConfig);
846: }
847:
848: /* Now try the users configuration file */
849: Configuration_Load(NULL);
850: }
851:
852: /*-----------------------------------------------------------------------*/
853: /**
854: * Set TOS etc information and initial help message
855: */
856: static void Main_StatusbarSetup(void)
857: {
858: const char *name = NULL;
859: SDLKey key;
860:
861: key = ConfigureParams.Shortcut.withoutModifier[SHORTCUT_OPTIONS];
862: if (!key)
863: key = ConfigureParams.Shortcut.withModifier[SHORTCUT_OPTIONS];
864: if (key)
865: name = SDL_GetKeyName(key);
866: if (name)
867: {
868: char message[24], *keyname;
1.1.1.18 root 869: #ifdef _MUDFLAP
1.1.1.21 root 870: __mf_register((void*)name, 32, __MF_TYPE_GUESS, "SDL keyname");
1.1.1.18 root 871: #endif
1.1.1.15 root 872: keyname = Str_ToUpper(strdup(name));
873: snprintf(message, sizeof(message), "Press %s for Options", keyname);
874: free(keyname);
875:
1.1.1.23 root 876: Statusbar_AddMessage(message, 5000);
1.1.1.15 root 877: }
878: /* update information loaded by Main_Init() */
879: Statusbar_UpdateInfo();
880: }
881:
1.1.1.24 root 882:
1.1.1.15 root 883: /**
1.1.1.13 root 884: * Main
1.1.1.15 root 885: *
886: * Note: 'argv' cannot be declared const, MinGW would then fail to link.
1.1.1.13 root 887: */
1.1 root 888: int main(int argc, char *argv[])
889: {
1.1.1.13 root 890: /* Generate random seed */
891: srand(time(NULL));
892:
1.1.1.24 root 893: /* Logs default to stderr at start */
894: Log_Default();
895:
1.1.1.13 root 896: /* Initialize directory strings */
897: Paths_Init(argv[0]);
898:
1.1.1.23 root 899: /* Init some HW components before parsing the configuration / parameters */
900: Main_Init_HW();
901:
902: /* Set default configuration values */
1.1.1.13 root 903: Configuration_SetDefault();
1.1.1.8 root 904:
1.1.1.13 root 905: /* Now load the values from the configuration file */
1.1.1.15 root 906: Main_LoadInitialConfig();
1.1 root 907:
1.1.1.15 root 908: /* Check for any passed parameters */
1.1.1.19 root 909: if (!Opt_ParseParameters(argc, (const char * const *)argv))
1.1.1.15 root 910: {
1.1.1.27! root 911: Control_RemoveFifo();
1.1.1.15 root 912: return 1;
913: }
1.1.1.17 root 914: /* monitor type option might require "reset" -> true */
915: Configuration_Apply(true);
1.1.1.2 root 916:
1.1.1.13 root 917: #ifdef WIN32
918: Win_OpenCon();
919: #endif
1.1.1.7 root 920:
1.1.1.13 root 921: #if HAVE_SETENV
1.1.1.20 root 922: /* Needed on maemo but useful also with normal X11 window managers for
923: * window grouping when you have multiple Hatari SDL windows open */
1.1.1.13 root 924: setenv("SDL_VIDEO_X11_WMCLASS", "hatari", 1);
1.1.1.20 root 925:
926: /* Needed for proper behavior of Caps Lock on some systems */
927: setenv("SDL_DISABLE_LOCK_KEYS", "1", 1);
1.1.1.13 root 928: #endif
1.1 root 929:
1.1.1.13 root 930: /* Init emulator system */
931: Main_Init();
1.1 root 932:
1.1.1.15 root 933: /* Set initial Statusbar information */
934: Main_StatusbarSetup();
935:
1.1.1.13 root 936: /* Check if SDL_Delay is accurate */
937: Main_CheckForAccurateDelays();
1.1.1.12 root 938:
1.1.1.22 root 939: if ( AviRecordOnStartup ) /* Immediately starts avi recording ? */
1.1.1.19 root 940: Avi_StartRecording ( ConfigureParams.Video.AviRecordFile , ConfigureParams.Screen.bCrop ,
941: ConfigureParams.Video.AviRecordFps == 0 ?
942: ClocksTimings_GetVBLPerSec ( ConfigureParams.System.nMachineType , nScreenRefreshRate ) :
943: (Uint32)ConfigureParams.Video.AviRecordFps << CLOCKS_TIMINGS_SHIFT_VBL ,
944: 1 << CLOCKS_TIMINGS_SHIFT_VBL ,
945: ConfigureParams.Video.AviRecordVcodec );
1.1.1.18 root 946:
1.1.1.13 root 947: /* Run emulation */
948: Main_UnPauseEmulation();
949: M68000_Start(); /* Start emulation */
1.1 root 950:
1.1.1.27! root 951: Control_RemoveFifo();
1.1.1.18 root 952: if (bRecordingAvi)
953: {
954: /* cleanly close the avi file */
955: Statusbar_AddMessage("Finishing AVI file...", 100);
1.1.1.23 root 956: Statusbar_Update(sdlscrn, true);
1.1.1.18 root 957: Avi_StopRecording();
958: }
1.1.1.13 root 959: /* Un-init emulation system */
960: Main_UnInit();
1.1 root 961:
1.1.1.23 root 962: return nQuitValue;
1.1 root 963: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.