Annotation of hatari/src/main.c, revision 1.1.1.27

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: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.