1333 lines
33 KiB
C
1333 lines
33 KiB
C
/*
|
|
Hatari - main.c
|
|
|
|
This file is distributed under the GNU General Public License, version 2
|
|
or at your option any later version. Read the file gpl.txt for details.
|
|
|
|
Main initialization and event handling routines.
|
|
*/
|
|
const char Main_fileid[] = "Hatari main.c : " __DATE__ " " __TIME__;
|
|
|
|
#include <time.h>
|
|
#include <errno.h>
|
|
#include <SDL.h>
|
|
|
|
#include "main.h"
|
|
#include "version.h"
|
|
#include "configuration.h"
|
|
#include "control.h"
|
|
#include "options.h"
|
|
#include "dialog.h"
|
|
#include "audio.h"
|
|
#include "joy.h"
|
|
#include "floppy.h"
|
|
#include "floppy_ipf.h"
|
|
#include "floppy_stx.h"
|
|
#include "gemdos.h"
|
|
#include "fdc.h"
|
|
#include "hdc.h"
|
|
#include "ide.h"
|
|
#include "acia.h"
|
|
#include "ikbd.h"
|
|
#include "ioMem.h"
|
|
#include "keymap.h"
|
|
#include "log.h"
|
|
#include "m68000.h"
|
|
#include "memorySnapShot.h"
|
|
#include "midi.h"
|
|
#include "nvram.h"
|
|
#include "paths.h"
|
|
#include "printer.h"
|
|
#include "reset.h"
|
|
#include "resolution.h"
|
|
#include "rs232.h"
|
|
#include "screen.h"
|
|
#include "sdlgui.h"
|
|
#include "shortcut.h"
|
|
#include "sound.h"
|
|
#include "dmaSnd.h"
|
|
#include "statusbar.h"
|
|
#include "stMemory.h"
|
|
#include "str.h"
|
|
#include "tos.h"
|
|
#include "video.h"
|
|
#include "avi_record.h"
|
|
#include "debugui.h"
|
|
#include "clocks_timings.h"
|
|
|
|
#include "hatari-glue.h"
|
|
|
|
#include "falcon/hostscreen.h"
|
|
#include "falcon/dsp.h"
|
|
|
|
#if HAVE_GETTIMEOFDAY
|
|
#include <sys/time.h>
|
|
#endif
|
|
|
|
#ifdef WIN32
|
|
#include "gui-win/opencon.h"
|
|
#endif
|
|
|
|
bool bQuitProgram = false; /* Flag to quit program cleanly */
|
|
static int nQuitValue; /* exit value */
|
|
|
|
static Uint32 nRunVBLs; /* Whether and how many VBLS to run before exit */
|
|
static Uint32 nFirstMilliTick; /* Ticks when VBL counting started */
|
|
static Uint32 nVBLCount; /* Frame count */
|
|
static int nVBLSlowdown = 1; /* host VBL wait multiplier */
|
|
|
|
static bool bEmulationActive = true; /* Run emulation when started */
|
|
static bool bAccurateDelays; /* Host system has an accurate SDL_Delay()? */
|
|
static bool bIgnoreNextMouseMotion = false; /* Next mouse motion will be ignored (needed after SDL_WarpMouse) */
|
|
|
|
#ifdef GEKKO
|
|
static int HatStKey[2][4];
|
|
static const int HatStArrow[4]={75,72,77,80};
|
|
bool Dpad_Dir[2][4];
|
|
bool MouseMode = false;
|
|
|
|
enum
|
|
{
|
|
WIIPLAYER1,
|
|
WIIPLAYER2
|
|
};
|
|
#endif
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Return current time as millisecond for performance measurements.
|
|
*
|
|
* (On Unix only time spent by Hatari itself is counted, on other
|
|
* platforms less accurate SDL "wall clock".)
|
|
*/
|
|
#if HAVE_SYS_TIMES_H
|
|
#include <unistd.h>
|
|
#include <sys/times.h>
|
|
static Uint32 Main_GetTicks(void)
|
|
{
|
|
static unsigned int ticks_to_msec = 0;
|
|
struct tms fields;
|
|
if (!ticks_to_msec)
|
|
{
|
|
ticks_to_msec = sysconf(_SC_CLK_TCK);
|
|
printf("OS clock ticks / second: %d\n", ticks_to_msec);
|
|
/* Linux has 100Hz virtual clock so no accuracy loss there */
|
|
ticks_to_msec = 1000UL / ticks_to_msec;
|
|
}
|
|
/* return milliseconds (clock ticks) spent in this process
|
|
*/
|
|
times(&fields);
|
|
return ticks_to_msec * fields.tms_utime;
|
|
}
|
|
#else
|
|
# warning "times() function missing, using inaccurate SDL_GetTicks() instead."
|
|
# define Main_GetTicks SDL_GetTicks
|
|
#endif
|
|
|
|
|
|
//#undef HAVE_GETTIMEOFDAY
|
|
//#undef HAVE_NANOSLEEP
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Return a time counter in micro seconds.
|
|
* If gettimeofday is available, we use it directly, else we convert the
|
|
* return of SDL_GetTicks in micro sec.
|
|
*/
|
|
|
|
static Sint64 Time_GetTicks ( void )
|
|
{
|
|
Sint64 ticks_micro;
|
|
|
|
#if HAVE_GETTIMEOFDAY
|
|
struct timeval now;
|
|
gettimeofday ( &now , NULL );
|
|
ticks_micro = (Sint64)now.tv_sec * 1000000 + now.tv_usec;
|
|
#else
|
|
ticks_micro = (Sint64)SDL_GetTicks() * 1000; /* milli sec -> micro sec */
|
|
#endif
|
|
|
|
return ticks_micro;
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Sleep for a given number of micro seconds.
|
|
* If nanosleep is available, we use it directly, else we use SDL_Delay
|
|
* (which is portable, but less accurate as is uses milli-seconds)
|
|
*/
|
|
|
|
static void Time_Delay ( Sint64 ticks_micro )
|
|
{
|
|
#if HAVE_NANOSLEEP
|
|
struct timespec ts;
|
|
int ret;
|
|
ts.tv_sec = ticks_micro / 1000000;
|
|
ts.tv_nsec = (ticks_micro % 1000000) * 1000; /* micro sec -> nano sec */
|
|
/* wait until all the delay is elapsed, including possible interruptions by signals */
|
|
do
|
|
{
|
|
errno = 0;
|
|
ret = nanosleep(&ts, &ts);
|
|
} while ( ret && ( errno == EINTR ) ); /* keep on sleeping if we were interrupted */
|
|
#else
|
|
SDL_Delay ( (Uint32)(ticks_micro / 1000) ) ; /* micro sec -> milli sec */
|
|
#endif
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Pause emulation, stop sound. 'visualize' should be set true,
|
|
* unless unpause will be called immediately afterwards.
|
|
*
|
|
* @return true if paused now, false if was already paused
|
|
*/
|
|
bool Main_PauseEmulation(bool visualize)
|
|
{
|
|
if ( !bEmulationActive )
|
|
return false;
|
|
|
|
Audio_EnableAudio(false);
|
|
bEmulationActive = false;
|
|
if (visualize)
|
|
{
|
|
if (nFirstMilliTick)
|
|
{
|
|
int interval = Main_GetTicks() - nFirstMilliTick;
|
|
static float previous;
|
|
float current;
|
|
|
|
current = (1000.0 * nVBLCount) / interval;
|
|
#ifndef GEKKO
|
|
printf("SPEED: %.1f VBL/s (%d/%.1fs), diff=%.1f%%\n",
|
|
current, nVBLCount, interval/1000.0,
|
|
previous>0.0 ? 100*(current-previous)/previous : 0.0);
|
|
#endif
|
|
nVBLCount = nFirstMilliTick = 0;
|
|
previous = current;
|
|
}
|
|
|
|
Statusbar_AddMessage("Emulation paused", 100);
|
|
/* make sure msg gets shown */
|
|
Statusbar_Update(sdlscrn, true);
|
|
|
|
if (bGrabMouse && !bInFullScreen)
|
|
/* Un-grab mouse pointer in windowed mode */
|
|
SDL_WM_GrabInput(SDL_GRAB_OFF);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Start/continue emulation
|
|
*
|
|
* @return true if continued, false if was already running
|
|
*/
|
|
bool Main_UnPauseEmulation(void)
|
|
{
|
|
if ( bEmulationActive )
|
|
return false;
|
|
|
|
Sound_BufferIndexNeedReset = true;
|
|
Audio_EnableAudio(ConfigureParams.Sound.bEnableSound);
|
|
bEmulationActive = true;
|
|
|
|
/* Cause full screen update (to clear all) */
|
|
Screen_SetFullUpdate();
|
|
|
|
if (bGrabMouse)
|
|
/* Grab mouse pointer again */
|
|
SDL_WM_GrabInput(SDL_GRAB_ON);
|
|
return true;
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Optionally ask user whether to quit and set bQuitProgram accordingly
|
|
*/
|
|
void Main_RequestQuit(int exitval)
|
|
{
|
|
if (ConfigureParams.Memory.bAutoSave)
|
|
{
|
|
bQuitProgram = true;
|
|
MemorySnapShot_Capture(ConfigureParams.Memory.szAutoSaveFileName, false);
|
|
}
|
|
else if (ConfigureParams.Log.bConfirmQuit)
|
|
{
|
|
bQuitProgram = false; /* if set true, dialog exits */
|
|
bQuitProgram = DlgAlert_Query("All unsaved data will be lost.\nDo you really want to quit?");
|
|
}
|
|
else
|
|
{
|
|
bQuitProgram = true;
|
|
}
|
|
|
|
if (bQuitProgram)
|
|
{
|
|
/* Assure that CPU core shuts down */
|
|
M68000_SetSpecial(SPCFLAG_BRK);
|
|
}
|
|
nQuitValue = exitval;
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Set how many VBLs Hatari should run, from the moment this function
|
|
* is called.
|
|
*/
|
|
void Main_SetRunVBLs(Uint32 vbls)
|
|
{
|
|
fprintf(stderr, "Exit after %d VBLs.\n", vbls);
|
|
nRunVBLs = vbls;
|
|
nVBLCount = 0;
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Set VBL wait slowdown factor/multiplayer
|
|
*/
|
|
bool Main_SetVBLSlowdown(int factor)
|
|
{
|
|
if (factor < 1 || factor > 8) {
|
|
fprintf(stderr, "ERROR: invalid VBL slowdown factor %d, should be 1-8!\n", factor);
|
|
return false;
|
|
}
|
|
fprintf(stderr, "Slow down host VBL wait by factor of %d.\n", factor);
|
|
nVBLSlowdown = factor;
|
|
return true;
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* This function waits on each emulated VBL to synchronize the real time
|
|
* with the emulated ST.
|
|
* Unfortunately SDL_Delay and other sleep functions like usleep or nanosleep
|
|
* are very inaccurate on some systems like Linux 2.4 or Mac OS X (they can only
|
|
* wait for a multiple of 10ms due to the scheduler on these systems), so we have
|
|
* to "busy wait" there to get an accurate timing.
|
|
* All times are expressed as micro seconds, to avoid too much rounding error.
|
|
*/
|
|
void Main_WaitOnVbl(void)
|
|
{
|
|
Sint64 CurrentTicks;
|
|
static Sint64 DestTicks = 0;
|
|
Sint64 FrameDuration_micro;
|
|
Sint64 nDelay;
|
|
|
|
nVBLCount++;
|
|
if (nRunVBLs && nVBLCount >= nRunVBLs)
|
|
{
|
|
/* show VBLs/s */
|
|
Main_PauseEmulation(true);
|
|
exit(0);
|
|
}
|
|
|
|
// FrameDuration_micro = (Sint64) ( 1000000.0 / nScreenRefreshRate + 0.5 ); /* round to closest integer */
|
|
FrameDuration_micro = ClocksTimings_GetVBLDuration_micro ( ConfigureParams.System.nMachineType , nScreenRefreshRate );
|
|
FrameDuration_micro *= nVBLSlowdown;
|
|
CurrentTicks = Time_GetTicks();
|
|
|
|
if (DestTicks == 0) /* on first call, init DestTicks */
|
|
{
|
|
DestTicks = CurrentTicks + FrameDuration_micro;
|
|
}
|
|
|
|
DestTicks += pulse_swallowing_count; /* audio.c - Audio_CallBack() */
|
|
|
|
nDelay = DestTicks - CurrentTicks;
|
|
|
|
/* Do not wait if we are in fast forward mode or if we are totally out of sync */
|
|
if (ConfigureParams.System.bFastForward == true
|
|
|| nDelay < -4*FrameDuration_micro || nDelay > 50*FrameDuration_micro)
|
|
{
|
|
if (ConfigureParams.System.bFastForward == true)
|
|
{
|
|
if (!nFirstMilliTick)
|
|
nFirstMilliTick = Main_GetTicks();
|
|
}
|
|
if (nFrameSkips < ConfigureParams.Screen.nFrameSkips)
|
|
{
|
|
nFrameSkips += 1;
|
|
// Log_Printf(LOG_DEBUG, "Increased frameskip to %d\n", nFrameSkips);
|
|
}
|
|
/* Only update DestTicks for next VBL */
|
|
DestTicks = CurrentTicks + FrameDuration_micro;
|
|
return;
|
|
}
|
|
/* If automatic frameskip is enabled and delay's more than twice
|
|
* the effect of single frameskip, decrease frameskip
|
|
*/
|
|
if (nFrameSkips > 0
|
|
&& ConfigureParams.Screen.nFrameSkips >= AUTO_FRAMESKIP_LIMIT
|
|
&& 2*nDelay > FrameDuration_micro/nFrameSkips)
|
|
{
|
|
nFrameSkips -= 1;
|
|
// Log_Printf(LOG_DEBUG, "Decreased frameskip to %d\n", nFrameSkips);
|
|
}
|
|
|
|
if (bAccurateDelays)
|
|
{
|
|
/* Accurate sleeping is possible -> use SDL_Delay to free the CPU */
|
|
if (nDelay > 1000)
|
|
Time_Delay(nDelay - 1000);
|
|
}
|
|
else
|
|
{
|
|
/* No accurate SDL_Delay -> only wait if more than 5ms to go... */
|
|
if (nDelay > 5000)
|
|
Time_Delay(nDelay<10000 ? nDelay-1000 : 9000);
|
|
}
|
|
|
|
/* Now busy-wait for the right tick: */
|
|
while (nDelay > 0)
|
|
{
|
|
CurrentTicks = Time_GetTicks();
|
|
nDelay = DestTicks - CurrentTicks;
|
|
/* If the delay is still bigger than one frame, somebody
|
|
* played tricks with the system clock and we have to abort */
|
|
if (nDelay > FrameDuration_micro)
|
|
break;
|
|
}
|
|
|
|
//printf ( "tick %lld\n" , CurrentTicks );
|
|
/* Update DestTicks for next VBL */
|
|
DestTicks += FrameDuration_micro;
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Since SDL_Delay and friends are very inaccurate on some systems, we have
|
|
* to check if we can rely on this delay function.
|
|
*/
|
|
static void Main_CheckForAccurateDelays(void)
|
|
{
|
|
int nStartTicks, nEndTicks;
|
|
|
|
/* Force a task switch now, so we have a longer timeslice afterwards */
|
|
SDL_Delay(10);
|
|
|
|
nStartTicks = SDL_GetTicks();
|
|
SDL_Delay(1);
|
|
nEndTicks = SDL_GetTicks();
|
|
|
|
/* If the delay took longer than 10ms, we are on an inaccurate system! */
|
|
bAccurateDelays = ((nEndTicks - nStartTicks) < 9);
|
|
|
|
if (bAccurateDelays)
|
|
Log_Printf(LOG_DEBUG, "Host system has accurate delays. (%d)\n", nEndTicks - nStartTicks);
|
|
else
|
|
Log_Printf(LOG_WARN, "Host system does not have accurate delays. (%d)\n", nEndTicks - nStartTicks);
|
|
}
|
|
|
|
|
|
/* ----------------------------------------------------------------------- */
|
|
/**
|
|
* Set mouse pointer to new x,y coordinates and set flag to ignore
|
|
* the mouse event that is generated by SDL_WarpMouse().
|
|
*
|
|
* Skip the request is it's not position restore and mouse warping is disabled.
|
|
*/
|
|
void Main_WarpMouse(int x, int y, bool restore)
|
|
{
|
|
if (!(restore || ConfigureParams.Screen.bMouseWarp))
|
|
return;
|
|
#if WITH_SDL2
|
|
SDL_WarpMouseInWindow(sdlWindow, x, y);
|
|
#else
|
|
SDL_WarpMouse(x, y);
|
|
#endif
|
|
bIgnoreNextMouseMotion = true;
|
|
}
|
|
|
|
|
|
/* ----------------------------------------------------------------------- */
|
|
/**
|
|
* Handle mouse motion event.
|
|
*/
|
|
static void Main_HandleMouseMotion(SDL_Event *pEvent)
|
|
{
|
|
int dx, dy;
|
|
static int ax = 0, ay = 0;
|
|
|
|
/* Ignore motion when position has changed right after a reset or TOS
|
|
* (especially version 4.04) might get confused and play key clicks */
|
|
if (bIgnoreNextMouseMotion || nVBLs < 10)
|
|
{
|
|
bIgnoreNextMouseMotion = false;
|
|
return;
|
|
}
|
|
|
|
dx = pEvent->motion.xrel;
|
|
dy = pEvent->motion.yrel;
|
|
|
|
/* In zoomed low res mode, we divide dx and dy by the zoom factor so that
|
|
* the ST mouse cursor stays in sync with the host mouse. However, we have
|
|
* to take care of lowest bit of dx and dy which will get lost when
|
|
* dividing. So we store these bits in ax and ay and add them to dx and dy
|
|
* the next time. */
|
|
if (nScreenZoomX != 1)
|
|
{
|
|
dx += ax;
|
|
ax = dx % nScreenZoomX;
|
|
dx /= nScreenZoomX;
|
|
}
|
|
if (nScreenZoomY != 1)
|
|
{
|
|
dy += ay;
|
|
ay = dy % nScreenZoomY;
|
|
dy /= nScreenZoomY;
|
|
}
|
|
|
|
KeyboardProcessor.Mouse.dx += dx;
|
|
KeyboardProcessor.Mouse.dy += dy;
|
|
}
|
|
|
|
#ifdef GEKKO
|
|
/* ----------------------------------------------------------------------- */
|
|
/**
|
|
* Simulate ST mouse motion.
|
|
*
|
|
*/
|
|
static void Main_MoveMouse(Sint16 *mousecoords)
|
|
{
|
|
SDL_Event fake_key_event;
|
|
|
|
fake_key_event.type = SDL_MOUSEMOTION;
|
|
fake_key_event.motion.xrel = mousecoords[0] / 640;
|
|
fake_key_event.motion.yrel = mousecoords[1] / 480;
|
|
SDL_PushEvent(&fake_key_event);
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------- */
|
|
/**
|
|
* Simulate ST mouse left and right click.
|
|
*
|
|
* JoyType : 0 = Wiimote/Classic, 1 = GameCube controller
|
|
*/
|
|
static void Main_PressMouseButton(int sdlport, int sdlbutton, Uint8 pushed)
|
|
{
|
|
int JoyType;
|
|
int mousebutton;
|
|
|
|
/* Wii buttons to ST mouse buttons. 0: left button, 1: right button */
|
|
int StMouse[2][19] = {
|
|
/* { A, B, 1, 2, -, +, H, Z, C, A, B, X, Y, L, R, ZL, ZR, -, +} */
|
|
//{-1, -1, -1, -1, -1, -1, -1, 0, 1, 0, 1, -1, -1, 0, 1, -1, -1, -1, -1}, // Wiimote / Classic controller 1 & 2
|
|
{-1, -1, 1, 0, -1, -1, -1, 0, 1, 0, 1, -1, -1, 0, 1, -1, -1, -1, -1}, // Wiimote / Classic controller 1 & 2
|
|
/* {A, B, X, Y, Z, R, L,START,...} */
|
|
{0, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,-1,-1,-1,-1} // Gamecube controller 1 & 2
|
|
};
|
|
|
|
if (sdlport == 0 || sdlport == 1)
|
|
{
|
|
JoyType = 0;
|
|
|
|
/* Don't use Wiimote button 1 & 2 for mouse button if an ST key is mapped on them */
|
|
if (sdlbutton == 2 && ConfigureParams.Joysticks.Joy[0].nKeytoJoyWii1Button[0] != 0)
|
|
JoyType = 1;
|
|
|
|
if (sdlbutton == 3 && ConfigureParams.Joysticks.Joy[0].nKeytoJoyWii1Button[1] != 0)
|
|
JoyType = 1;
|
|
}
|
|
else if (sdlport == 4 || sdlport == 5)
|
|
{
|
|
JoyType = 1;
|
|
}
|
|
|
|
mousebutton = StMouse[JoyType][sdlbutton];
|
|
|
|
if (mousebutton == -1)
|
|
return;
|
|
|
|
if (!pushed)
|
|
{
|
|
if (mousebutton == 0)
|
|
{
|
|
Keyboard.bLButtonDown &= ~BUTTON_MOUSE;
|
|
}
|
|
else if (mousebutton == 1)
|
|
{
|
|
Keyboard.bRButtonDown &= ~BUTTON_MOUSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (mousebutton == 0)
|
|
{
|
|
if (Keyboard.LButtonDblClk == 0)
|
|
Keyboard.bLButtonDown |= BUTTON_MOUSE;
|
|
}
|
|
else if (mousebutton == 1)
|
|
{
|
|
Keyboard.bRButtonDown |= BUTTON_MOUSE;
|
|
}
|
|
}
|
|
}
|
|
/* ----------------------------------------------------------------------- */
|
|
/**
|
|
* Handle Wii buttons event.
|
|
*
|
|
* Press the corresponding ST key configured in the Wii mapper.
|
|
* WiiButtonBinding contains the Wii buttons that can be configured.
|
|
* StKey contains all the ST keys mapped to the Wii buttons.
|
|
* JoyType : Wiimote and Classic controller(0) or GameCube Pad(1).
|
|
* JoyIndex : Wii controller index.
|
|
*/
|
|
static void Main_HandleWiiButton(int sdlport, int sdlbutton, Uint8 pushed)
|
|
{
|
|
int j;
|
|
int JoyType;
|
|
int KeyIndex;
|
|
int JoyIndex;
|
|
int StKey[4][9];
|
|
/* Wii buttons configured in the mapper */
|
|
int WiiButtonBinding[2][19] = {
|
|
/* {*, *, 1, 2, -, *, *, *, *, *, B, X, Y, *, *, *, *, *, *} */
|
|
{-1, -1, 0, 1, 2, -1, -1, -1, -1, -1, 3, 4, 5, -1, -1, -1, -1, -1, -1}, // Wiimote / Classic controller 1 & 2
|
|
/* {*, *, X, Y, *, R, *, *, *, *, *, *, *, *, *, *, *, *, *} */
|
|
{-1, -1, 6, 7, -1, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1} // Gamecube controller 1 & 2
|
|
};
|
|
|
|
switch (sdlport)
|
|
{
|
|
/* Wiimote/Classic 1 */
|
|
case 0:
|
|
//if (MouseMode && sdlbutton == 0)
|
|
// return;
|
|
JoyType = 0;
|
|
JoyIndex = 0;
|
|
KeyIndex = WiiButtonBinding[JoyType][sdlbutton];
|
|
|
|
if (MouseMode)
|
|
{
|
|
/* In mouse mode never send Wiimote A button. */
|
|
if (sdlbutton == 0)
|
|
return;
|
|
|
|
/* Button 1 & 2 are also used for mouse buttons, disable them here if there are no bindings */
|
|
if (sdlbutton == 2 && ConfigureParams.Joysticks.Joy[0].nKeytoJoyWii1Button[0] == 0)
|
|
KeyIndex = -1;
|
|
|
|
if (sdlbutton == 3 && ConfigureParams.Joysticks.Joy[0].nKeytoJoyWii1Button[1] == 0)
|
|
KeyIndex = -1;
|
|
}
|
|
break;
|
|
/* Wiimote/Classic 2 */
|
|
case 1:
|
|
JoyType = 0;
|
|
JoyIndex = 1;
|
|
KeyIndex = WiiButtonBinding[JoyType][sdlbutton];
|
|
break;
|
|
/* GameCube Pad 1*/
|
|
case 4:
|
|
if (MouseMode && sdlbutton == 0)
|
|
return;
|
|
JoyType = 1;
|
|
JoyIndex = 2;
|
|
KeyIndex = WiiButtonBinding[JoyType][sdlbutton];
|
|
break;
|
|
/* GameCube Pad 2*/
|
|
case 5:
|
|
JoyType = 1;
|
|
JoyIndex = 3;
|
|
KeyIndex = WiiButtonBinding[JoyType][sdlbutton];
|
|
break;
|
|
}
|
|
|
|
/* Retrieve Wiimotes/Classics bindings */
|
|
for(j=0;j<6;j++)
|
|
{
|
|
StKey[0][j] = ConfigureParams.Joysticks.Joy[0].nKeytoJoyWii1Button[j];
|
|
StKey[1][j] = ConfigureParams.Joysticks.Joy[0].nKeytoJoyWii2Button[j];
|
|
}
|
|
|
|
/* Retrieve GameCube Pads bindings */
|
|
for(j=6;j<9;j++)
|
|
{
|
|
StKey[2][j] = ConfigureParams.Joysticks.Joy[0].nKeytoJoyWii1Button[j];
|
|
StKey[3][j] = ConfigureParams.Joysticks.Joy[0].nKeytoJoyWii2Button[j];
|
|
}
|
|
|
|
if (KeyIndex == -1)
|
|
return;
|
|
|
|
if (StKey[JoyIndex][KeyIndex] == -1)
|
|
return;
|
|
|
|
/* Press configured ST key if any */
|
|
if (!pushed)
|
|
{
|
|
IKBD_PressSTKey(StKey[JoyIndex][KeyIndex], false);
|
|
}
|
|
else
|
|
{
|
|
IKBD_PressSTKey(StKey[JoyIndex][KeyIndex], true);
|
|
}
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------- */
|
|
/**
|
|
* Handle Wii hats event.
|
|
*
|
|
* Press the corresponding ST key configured in the Wii mapper.
|
|
* JoyType : controllers grouped by player #. Each player has
|
|
* wiimote/classic/gc pad. The hats bindings are the same on those 3
|
|
* controllers.
|
|
*
|
|
*/
|
|
static void Main_HandleWiiHat(int JoyType)
|
|
{
|
|
int i;
|
|
bool pushed = false;
|
|
|
|
for (i=0; i < 4; i++)
|
|
{
|
|
if (Dpad_Dir[JoyType][i])
|
|
{
|
|
pushed = true;
|
|
|
|
/* Wiimotes/Classics */
|
|
if (JoyType == 0)
|
|
{
|
|
/* FIXME : Last minute bug, ST arrows aren't sent correctly here
|
|
* For now, it's handled in joy.c with Joy_WiiHatArrow*/
|
|
if (Joy1HatMode == 1) // ST key mode
|
|
IKBD_PressSTKey(HatStKey[WIIPLAYER1][i], pushed);
|
|
//else if (Joy1HatMode == 2) // ST arrows mode
|
|
// IKBD_PressSTKey(HatStArrow[i], pushed);
|
|
}
|
|
/* GameCube Pads */
|
|
if (JoyType == 1)
|
|
{
|
|
if (Joy2HatMode == 1) // ST key mode
|
|
IKBD_PressSTKey(HatStKey[WIIPLAYER2][i], pushed);
|
|
//else if (Joy2HatMode == 2) // ST arrows mode
|
|
// IKBD_PressSTKey(HatStArrow[i], pushed);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------- */
|
|
/**
|
|
* Release ST keys.
|
|
*
|
|
*/
|
|
static void Clear_IKBD(int JoyType)
|
|
{
|
|
int i;
|
|
|
|
if(JoyType == WIIPLAYER1)
|
|
{
|
|
if (Joy1HatMode == 1) // ST key mode
|
|
{
|
|
for (i=0;i<4;i++)
|
|
IKBD_PressSTKey(HatStKey[WIIPLAYER1][i], false);
|
|
}
|
|
|
|
if (Joy1HatMode == 2) // ST arrows mode
|
|
{
|
|
for (i=0;i<4;i++)
|
|
IKBD_PressSTKey(HatStArrow[i], false);
|
|
}
|
|
}
|
|
else if(JoyType == WIIPLAYER2)
|
|
{
|
|
if (Joy2HatMode == 1) // ST key mode
|
|
{
|
|
for (i=0;i<4;i++)
|
|
IKBD_PressSTKey(HatStKey[WIIPLAYER2][i], false);
|
|
}
|
|
|
|
if (Joy2HatMode == 2) // ST arrows mode
|
|
{
|
|
for (i=0;i<4;i++)
|
|
IKBD_PressSTKey(HatStArrow[i], false);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/* ----------------------------------------------------------------------- */
|
|
/**
|
|
* SDL message handler.
|
|
* Here we process the SDL events (keyboard, mouse, ...) and map it to
|
|
* Atari IKBD events.
|
|
*/
|
|
void Main_EventHandler(void)
|
|
{
|
|
bool bContinueProcessing;
|
|
SDL_Event event;
|
|
int events;
|
|
int remotepause;
|
|
|
|
#ifdef GEKKO
|
|
int i, nPadsConnected;
|
|
int port = -1;
|
|
|
|
/* ST mouse emulation with stick/dpad */
|
|
MouseMode = (ConfigureParams.Joysticks.Joy[1].nJoystickMode == 0) ? 1 : 0;
|
|
Sint16 mousecoords[2] = {0, 0};
|
|
|
|
static SDL_Joystick *sdlJoystick[5] = /* SDL's joystick structures */
|
|
{
|
|
NULL, NULL, NULL, NULL, NULL
|
|
};
|
|
|
|
/* Reset Wii hats */
|
|
for (i=0; i<4; i++)
|
|
{
|
|
Dpad_Dir[0][i] = false;
|
|
Dpad_Dir[1][i] = false;
|
|
}
|
|
|
|
/* Get ST keys assigned to Wii hats */
|
|
for(i=0; i<4; i++)
|
|
{
|
|
HatStKey[WIIPLAYER1][i] = ConfigureParams.Joysticks.Joy[0].nKeytoJoyWii1Hat[i];
|
|
HatStKey[WIIPLAYER2][i] = ConfigureParams.Joysticks.Joy[0].nKeytoJoyWii2Hat[i];
|
|
}
|
|
|
|
nPadsConnected = SDL_NumJoysticks();
|
|
for (i = 0; i < nPadsConnected && i < 5; i++)
|
|
{
|
|
/* Open the joysticks for use */
|
|
sdlJoystick[i] = SDL_JoystickOpen(i);
|
|
}
|
|
#endif
|
|
|
|
|
|
do
|
|
{
|
|
bContinueProcessing = false;
|
|
|
|
/* check remote process control */
|
|
remotepause = Control_CheckUpdates();
|
|
|
|
if ( bEmulationActive || remotepause )
|
|
{
|
|
events = SDL_PollEvent(&event);
|
|
|
|
}
|
|
else
|
|
{
|
|
ShortCut_ActKey();
|
|
/* last (shortcut) event activated emulation? */
|
|
if ( bEmulationActive )
|
|
break;
|
|
events = SDL_WaitEvent(&event);
|
|
}
|
|
if (!events)
|
|
{
|
|
/* no events -> if emulation is active or
|
|
* user is quitting -> return from function.
|
|
*/
|
|
continue;
|
|
}
|
|
|
|
|
|
switch (event.type)
|
|
{
|
|
case SDL_QUIT:
|
|
Main_RequestQuit(0);
|
|
break;
|
|
|
|
case SDL_MOUSEMOTION: /* Read/Update internal mouse position */
|
|
Main_HandleMouseMotion(&event);
|
|
bContinueProcessing = true;
|
|
break;
|
|
|
|
case SDL_MOUSEBUTTONDOWN:
|
|
if (event.button.button == SDL_BUTTON_LEFT)
|
|
{
|
|
if (Keyboard.LButtonDblClk == 0)
|
|
Keyboard.bLButtonDown |= BUTTON_MOUSE; /* Set button down flag */
|
|
}
|
|
else if (event.button.button == SDL_BUTTON_RIGHT)
|
|
{
|
|
Keyboard.bRButtonDown |= BUTTON_MOUSE;
|
|
}
|
|
else if (event.button.button == SDL_BUTTON_MIDDLE)
|
|
{
|
|
/* Start double-click sequence in emulation time */
|
|
Keyboard.LButtonDblClk = 1;
|
|
}
|
|
#if !WITH_SDL2
|
|
else if (event.button.button == SDL_BUTTON_WHEELDOWN)
|
|
{
|
|
/* Simulate pressing the "cursor down" key */
|
|
IKBD_PressSTKey(0x50, true);
|
|
}
|
|
else if (event.button.button == SDL_BUTTON_WHEELUP)
|
|
{
|
|
/* Simulate pressing the "cursor up" key */
|
|
IKBD_PressSTKey(0x48, true);
|
|
}
|
|
#endif
|
|
break;
|
|
|
|
case SDL_MOUSEBUTTONUP:
|
|
if (event.button.button == SDL_BUTTON_LEFT)
|
|
{
|
|
Keyboard.bLButtonDown &= ~BUTTON_MOUSE;
|
|
}
|
|
else if (event.button.button == SDL_BUTTON_RIGHT)
|
|
{
|
|
Keyboard.bRButtonDown &= ~BUTTON_MOUSE;
|
|
}
|
|
#if !WITH_SDL2
|
|
else if (event.button.button == SDL_BUTTON_WHEELDOWN)
|
|
{
|
|
/* Simulate releasing the "cursor down" key */
|
|
IKBD_PressSTKey(0x50, false);
|
|
}
|
|
else if (event.button.button == SDL_BUTTON_WHEELUP)
|
|
{
|
|
/* Simulate releasing the "cursor up" key */
|
|
IKBD_PressSTKey(0x48, false);
|
|
}
|
|
#endif
|
|
break;
|
|
|
|
case SDL_KEYDOWN:
|
|
Keymap_KeyDown(&event.key.keysym);
|
|
break;
|
|
|
|
case SDL_KEYUP:
|
|
Keymap_KeyUp(&event.key.keysym);
|
|
break;
|
|
|
|
case SDL_JOYBUTTONDOWN:
|
|
case SDL_JOYBUTTONUP:
|
|
port = event.jhat.which;
|
|
|
|
/* Send ST mouse clicks */
|
|
if (MouseMode)
|
|
Main_PressMouseButton(port, event.jbutton.button, event.type==SDL_JOYBUTTONDOWN);
|
|
//else
|
|
// Main_PressMouseButton(port, event.jbutton.button, 0);
|
|
|
|
/* Send ST keys configured in the Wii mapper */
|
|
Main_HandleWiiButton(port, event.jbutton.button, event.type==SDL_JOYBUTTONDOWN);
|
|
break;
|
|
|
|
case SDL_JOYHATMOTION:
|
|
if (!MouseMode)
|
|
{
|
|
/* port is player id here 0 or 1. SDL reports 1st wiimote/gc pad as 0/4 */
|
|
/* and 2d wiimote/gc pad as 1/5. */
|
|
port = event.jhat.which & 1;
|
|
|
|
Clear_IKBD(port);
|
|
|
|
if (event.jhat.value & SDL_HAT_LEFT) Dpad_Dir[port][0] = true;
|
|
if (event.jhat.value & SDL_HAT_UP) Dpad_Dir[port][1] = true;
|
|
if (event.jhat.value & SDL_HAT_RIGHT) Dpad_Dir[port][2] = true;
|
|
if (event.jhat.value & SDL_HAT_DOWN) Dpad_Dir[port][3] = true;
|
|
}
|
|
break;
|
|
|
|
case SDL_JOYAXISMOTION:
|
|
if (MouseMode)
|
|
{
|
|
if (event.jaxis.axis==0 || event.jaxis.axis==1)
|
|
mousecoords[event.jaxis.axis] = event.jaxis.value;
|
|
|
|
bContinueProcessing = true;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
/* don't let unknown events delay event processing */
|
|
bContinueProcessing = true;
|
|
break;
|
|
}
|
|
|
|
if (MouseMode)
|
|
{
|
|
/* Move ST mouse with hats on Wiimote/GC pad 1 */
|
|
if ((SDL_JoystickGetHat(sdlJoystick[0], 0) & SDL_HAT_LEFT)
|
|
|| (SDL_JoystickGetHat(sdlJoystick[4], 0) & SDL_HAT_LEFT)) mousecoords[0] = -3000;
|
|
if ((SDL_JoystickGetHat(sdlJoystick[0], 0) & SDL_HAT_RIGHT)
|
|
|| (SDL_JoystickGetHat(sdlJoystick[4], 0) & SDL_HAT_RIGHT)) mousecoords[0] = 3000;
|
|
if ((SDL_JoystickGetHat(sdlJoystick[0], 0) & SDL_HAT_UP )
|
|
|| (SDL_JoystickGetHat(sdlJoystick[4], 0) & SDL_HAT_UP)) mousecoords[1] = -2000;
|
|
if ((SDL_JoystickGetHat(sdlJoystick[0], 0) & SDL_HAT_DOWN)
|
|
|| (SDL_JoystickGetHat(sdlJoystick[4], 0) & SDL_HAT_DOWN)) mousecoords[1] = 2000;
|
|
}
|
|
else
|
|
{
|
|
port = event.jhat.which & 1;
|
|
/* 2 modes handled : ST arrows(Not anymore!) or ST keys configured in the mapper. */
|
|
/* ST joystick is handled in joy.c */
|
|
Main_HandleWiiHat(port);
|
|
}
|
|
|
|
} while (bContinueProcessing || !(bEmulationActive || bQuitProgram));
|
|
|
|
if (mousecoords[0] || mousecoords[1])
|
|
Main_MoveMouse(mousecoords);
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Set Hatari window title. Use NULL for default
|
|
*/
|
|
void Main_SetTitle(const char *title)
|
|
{
|
|
#if WITH_SDL2
|
|
if (title)
|
|
SDL_SetWindowTitle(sdlWindow, title);
|
|
else
|
|
SDL_SetWindowTitle(sdlWindow, PROG_NAME);
|
|
#else
|
|
if (title)
|
|
SDL_WM_SetCaption(title, "Hatari");
|
|
else
|
|
SDL_WM_SetCaption(PROG_NAME, "Hatari");
|
|
#endif
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Initialise emulation for some hardware components
|
|
* It is required to init those parts before parsing the parameters
|
|
* (for example, we should init FDC before inserting a disk and we
|
|
* need to know valid joysticks before selecting default joystick IDs)
|
|
*/
|
|
static void Main_Init_HW(void)
|
|
{
|
|
Joy_Init();
|
|
FDC_Init();
|
|
STX_Init();
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Initialise emulation
|
|
*/
|
|
static void Main_Init(void)
|
|
{
|
|
/* Open debug log file */
|
|
if (!Log_Init())
|
|
{
|
|
fprintf(stderr, "Logging/tracing initialization failed\n");
|
|
exit(-1);
|
|
}
|
|
Log_Printf(LOG_INFO, PROG_NAME ", compiled on: " __DATE__ ", " __TIME__ "\n");
|
|
|
|
/* Init SDL's video subsystem. Note: Audio subsystem
|
|
will be initialized later (failure not fatal). */
|
|
if (SDL_Init(SDL_INIT_VIDEO | Opt_GetNoParachuteFlag()) < 0)
|
|
{
|
|
fprintf(stderr, "Could not initialize the SDL library:\n %s\n", SDL_GetError() );
|
|
exit(-1);
|
|
}
|
|
|
|
if ( IPF_Init() != true )
|
|
{
|
|
fprintf(stderr, "Could not initialize the IPF support\n" );
|
|
exit(-1);
|
|
}
|
|
|
|
ClocksTimings_InitMachine ( ConfigureParams.System.nMachineType );
|
|
Resolution_Init();
|
|
SDLGui_Init();
|
|
Printer_Init();
|
|
RS232_Init();
|
|
Midi_Init();
|
|
Control_CheckUpdates(); /* enable window embedding? */
|
|
Screen_Init();
|
|
Main_SetTitle(NULL);
|
|
HostScreen_Init();
|
|
|
|
ACIA_Init( ACIA_Array , MachineClocks.ACIA_Freq , MachineClocks.ACIA_Freq );
|
|
IKBD_Init(); /* After ACIA_Init */
|
|
|
|
DSP_Init();
|
|
Floppy_Init();
|
|
M68000_Init(); /* Init CPU emulation */
|
|
Audio_Init();
|
|
Keymap_Init();
|
|
|
|
/* Init HD emulation */
|
|
HDC_Init();
|
|
Ide_Init();
|
|
GemDOS_Init();
|
|
if (ConfigureParams.HardDisk.bUseHardDiskDirectories)
|
|
{
|
|
/* uses variables set by HDC_Init()! */
|
|
GemDOS_InitDrives();
|
|
}
|
|
|
|
if (Reset_Cold()) /* Reset all systems, load TOS image */
|
|
{
|
|
/* If loading of the TOS failed, we bring up the GUI to let the
|
|
* user choose another TOS ROM file. */
|
|
Dialog_DoProperty();
|
|
}
|
|
if (!bTosImageLoaded || bQuitProgram)
|
|
{
|
|
fprintf(stderr, "Failed to load TOS image!\n");
|
|
SDL_Quit();
|
|
exit(-2);
|
|
}
|
|
|
|
IoMem_Init();
|
|
NvRam_Init();
|
|
Sound_Init();
|
|
|
|
/* done as last, needs CPU & DSP running... */
|
|
DebugUI_Init();
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Un-Initialise emulation
|
|
*/
|
|
static void Main_UnInit(void)
|
|
{
|
|
Screen_ReturnFromFullScreen();
|
|
Floppy_UnInit();
|
|
HDC_UnInit();
|
|
Midi_UnInit();
|
|
RS232_UnInit();
|
|
Printer_UnInit();
|
|
IoMem_UnInit();
|
|
NvRam_UnInit();
|
|
GemDOS_UnInitDrives();
|
|
Ide_UnInit();
|
|
Joy_UnInit();
|
|
if (Sound_AreWeRecording())
|
|
Sound_EndRecording();
|
|
Audio_UnInit();
|
|
SDLGui_UnInit();
|
|
DSP_UnInit();
|
|
HostScreen_UnInit();
|
|
Screen_UnInit();
|
|
Exit680x0();
|
|
|
|
IPF_Exit();
|
|
|
|
/* SDL uninit: */
|
|
SDL_Quit();
|
|
|
|
/* Close debug log file */
|
|
Log_UnInit();
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Load initial configuration file(s)
|
|
*/
|
|
static void Main_LoadInitialConfig(void)
|
|
{
|
|
char *psGlobalConfig;
|
|
|
|
psGlobalConfig = malloc(FILENAME_MAX);
|
|
if (psGlobalConfig)
|
|
{
|
|
#if defined(__AMIGAOS4__)
|
|
strncpy(psGlobalConfig, CONFDIR"hatari.cfg", FILENAME_MAX);
|
|
#else
|
|
snprintf(psGlobalConfig, FILENAME_MAX, CONFDIR"%chatari.cfg", PATHSEP);
|
|
#endif
|
|
/* Try to load the global configuration file */
|
|
Configuration_Load(psGlobalConfig);
|
|
|
|
free(psGlobalConfig);
|
|
}
|
|
|
|
/* Now try the users configuration file */
|
|
Configuration_Load(NULL);
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Set TOS etc information and initial help message
|
|
*/
|
|
static void Main_StatusbarSetup(void)
|
|
{
|
|
const char *name = NULL;
|
|
SDLKey key;
|
|
|
|
key = ConfigureParams.Shortcut.withoutModifier[SHORTCUT_OPTIONS];
|
|
if (!key)
|
|
key = ConfigureParams.Shortcut.withModifier[SHORTCUT_OPTIONS];
|
|
if (key)
|
|
name = SDL_GetKeyName(key);
|
|
if (name)
|
|
{
|
|
char message[24], *keyname;
|
|
#ifdef _MUDFLAP
|
|
__mf_register((void*)name, 32, __MF_TYPE_GUESS, "SDL keyname");
|
|
#endif
|
|
keyname = Str_ToUpper(strdup(name));
|
|
snprintf(message, sizeof(message), "Press %s for Options", keyname);
|
|
free(keyname);
|
|
|
|
Statusbar_AddMessage(message, 5000);
|
|
}
|
|
/* update information loaded by Main_Init() */
|
|
Statusbar_UpdateInfo();
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/**
|
|
* Initialize Wii controllers hats mode
|
|
* There are 3 modes available for Player 1 and 2(Joy1HatMode, Joy2HatMode)
|
|
* 0: ST joystick, 1: ST key, 2: ST arrows
|
|
*/
|
|
static void Main_InitJoyHatMode(void)
|
|
{
|
|
int i;
|
|
JOYSTICKMODE state;
|
|
|
|
state = ConfigureParams.Joysticks.Joy[1].nJoystickMode;
|
|
|
|
/* Joystick in port 1 enabled by default */
|
|
//if (state == JOYSTICK_KEYBOARD || state == JOYSTICK_DISABLED)
|
|
// ConfigureParams.Joysticks.Joy[1].nJoystickMode = JOYSTICK_REALSTICK;
|
|
|
|
for (i=0; i<4; i++)
|
|
{
|
|
/* Check for at least one assigned key to determine mode */
|
|
if (ConfigureParams.Joysticks.Joy[0].nKeytoJoyWii1Hat[i] != 0)
|
|
{
|
|
Joy1HatMode = 1; // Player 1 Hat ST key mode
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
Joy1HatMode = 0; // Player 1 Hat ST joystick mode
|
|
}
|
|
}
|
|
|
|
for (i=0; i<4; i++)
|
|
{
|
|
/* Check for at least one assigned key to determine mode */
|
|
if (ConfigureParams.Joysticks.Joy[0].nKeytoJoyWii2Hat[i] != 0)
|
|
{
|
|
Joy2HatMode = 1; // Player 2 Hat ST key mode
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
Joy2HatMode = 0; // Player 2 Hat ST joystick mode
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Main
|
|
*
|
|
* Note: 'argv' cannot be declared const, MinGW would then fail to link.
|
|
*/
|
|
int main(int argc, char *argv[])
|
|
{
|
|
#ifdef GEKKO
|
|
bool WiimoteConnected = true;
|
|
|
|
__exception_setreload(3);
|
|
|
|
fatInitDefault();
|
|
#endif
|
|
|
|
/* Generate random seed */
|
|
srand(time(NULL));
|
|
|
|
/* Logs default to stderr at start */
|
|
Log_Default();
|
|
|
|
/* Initialize directory strings */
|
|
Paths_Init(argv[0]);
|
|
|
|
/* Init some HW components before parsing the configuration / parameters */
|
|
Main_Init_HW();
|
|
|
|
/* Set default configuration values */
|
|
Configuration_SetDefault();
|
|
|
|
/* Now load the values from the configuration file */
|
|
Main_LoadInitialConfig();
|
|
|
|
/* Check for any passed parameters */
|
|
if (!Opt_ParseParameters(argc, (const char * const *)argv))
|
|
{
|
|
return 1;
|
|
}
|
|
/* monitor type option might require "reset" -> true */
|
|
Configuration_Apply(true);
|
|
|
|
#ifdef WIN32
|
|
Win_OpenCon();
|
|
#endif
|
|
|
|
#if HAVE_SETENV
|
|
/* Needed on maemo but useful also with normal X11 window managers for
|
|
* window grouping when you have multiple Hatari SDL windows open */
|
|
setenv("SDL_VIDEO_X11_WMCLASS", "hatari", 1);
|
|
|
|
/* Needed for proper behavior of Caps Lock on some systems */
|
|
setenv("SDL_DISABLE_LOCK_KEYS", "1", 1);
|
|
#endif
|
|
|
|
/* Init emulator system */
|
|
Main_Init();
|
|
|
|
#ifdef GEKKO
|
|
SDL_Delay(600);
|
|
/* Check if wiimote 1 is available, otherwise try GameCube pad 1 */
|
|
WiimoteConnected = Joy_CheckWiimote();
|
|
|
|
if (WiimoteConnected)
|
|
{
|
|
ConfigureParams.Joysticks.Joy[1].nJoyId = 0;
|
|
}
|
|
else
|
|
{
|
|
ConfigureParams.Joysticks.Joy[1].nJoyId = 4;
|
|
}
|
|
|
|
/* Init Joysticks hats mode */
|
|
Main_InitJoyHatMode();
|
|
#endif
|
|
|
|
/* Set initial Statusbar information */
|
|
Main_StatusbarSetup();
|
|
|
|
/* Check if SDL_Delay is accurate */
|
|
Main_CheckForAccurateDelays();
|
|
|
|
if ( AviRecordOnStartup ) /* Immediately starts avi recording ? */
|
|
Avi_StartRecording ( ConfigureParams.Video.AviRecordFile , ConfigureParams.Screen.bCrop ,
|
|
ConfigureParams.Video.AviRecordFps == 0 ?
|
|
ClocksTimings_GetVBLPerSec ( ConfigureParams.System.nMachineType , nScreenRefreshRate ) :
|
|
(Uint32)ConfigureParams.Video.AviRecordFps << CLOCKS_TIMINGS_SHIFT_VBL ,
|
|
1 << CLOCKS_TIMINGS_SHIFT_VBL ,
|
|
ConfigureParams.Video.AviRecordVcodec );
|
|
|
|
/* Run emulation */
|
|
Main_UnPauseEmulation();
|
|
M68000_Start(); /* Start emulation */
|
|
|
|
if (bRecordingAvi)
|
|
{
|
|
/* cleanly close the avi file */
|
|
Statusbar_AddMessage("Finishing AVI file...", 100);
|
|
Statusbar_Update(sdlscrn, true);
|
|
Avi_StopRecording();
|
|
}
|
|
/* Un-init emulation system */
|
|
Main_UnInit();
|
|
|
|
return nQuitValue;
|
|
}
|