From e4c22f2815f023da4fb5700581d3f19bf1d697bc Mon Sep 17 00:00:00 2001 From: stephena Date: Wed, 16 Jun 2004 00:31:32 +0000 Subject: [PATCH] Disabled switching to fullscreen mode in Windows when using OpenGL. Something is seriously screwed up there, and I'm not even attempting to get it working for this next release. It seems that sometimes switching to fullscreen in Windows in software mode causes crashes as well. I really hate Windows ... git-svn-id: svn://svn.code.sf.net/p/stella/code/trunk@268 8b62c5a3-ac7e-4cc8-8f21-d9a121418aba --- stella/src/common/mainSDL.cxx | 2043 +++++++++++++++++---------------- 1 file changed, 1028 insertions(+), 1015 deletions(-) diff --git a/stella/src/common/mainSDL.cxx b/stella/src/common/mainSDL.cxx index 5b61e5dfc..2a4799c20 100644 --- a/stella/src/common/mainSDL.cxx +++ b/stella/src/common/mainSDL.cxx @@ -1,1015 +1,1028 @@ -//============================================================================ -// -// SSSS tt lll lll -// SS SS tt ll ll -// SS tttttt eeee ll ll aaaa -// SSSS tt ee ee ll ll aa -// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" -// SS SS tt ee ll ll aa aa -// SSSS ttt eeeee llll llll aaaaa -// -// Copyright (c) 1995-2004 by Bradford W. Mott -// -// See the file "license" for information on usage and redistribution of -// this file, and for a DISCLAIMER OF ALL WARRANTIES. -// -// $Id: mainSDL.cxx,v 1.5 2004-06-13 18:14:03 stephena Exp $ -//============================================================================ - -#include -#include -#include -#include -#include - -#ifdef HAVE_GETTIMEOFDAY - #include - #include -#endif - -#include - -#include "bspf.hxx" -#include "Console.hxx" -#include "Event.hxx" -#include "StellaEvent.hxx" -#include "EventHandler.hxx" -#include "FrameBuffer.hxx" -#include "FrameBufferSDL.hxx" -#include "FrameBufferSoft.hxx" -#include "PropsSet.hxx" -#include "Sound.hxx" -#include "SoundSDL.hxx" -#include "Settings.hxx" - -#ifdef DISPLAY_OPENGL - #include "FrameBufferGL.hxx" - - // Indicates whether to use OpenGL mode - static bool theUseOpenGLFlag; -#endif - -#if defined(UNIX) - #include "SettingsUNIX.hxx" -#elif defined(WIN32) - #include "SettingsWin32.hxx" -#else - #error Unsupported platform! -#endif - -static void cleanup(); -static bool setupJoystick(); -static void handleEvents(); -static uInt32 getTicks(); -static bool setupProperties(PropertiesSet& set); - -#ifdef JOYSTICK_SUPPORT -// static uInt32 thePaddleNumber; - - // Lookup table for joystick numbers and events - StellaEvent::JoyStick joyList[StellaEvent::LastJSTICK] = { - StellaEvent::JSTICK_0, StellaEvent::JSTICK_1, StellaEvent::JSTICK_2, - StellaEvent::JSTICK_3, StellaEvent::JSTICK_5, StellaEvent::JSTICK_5 - }; - StellaEvent::JoyCode joyButtonList[StellaEvent::LastJCODE] = { - StellaEvent::JBUTTON_0, StellaEvent::JBUTTON_1, StellaEvent::JBUTTON_2, - StellaEvent::JBUTTON_3, StellaEvent::JBUTTON_4, StellaEvent::JBUTTON_5, - StellaEvent::JBUTTON_6, StellaEvent::JBUTTON_7, StellaEvent::JBUTTON_8, - StellaEvent::JBUTTON_9, StellaEvent::JBUTTON_10, StellaEvent::JBUTTON_11, - StellaEvent::JBUTTON_12, StellaEvent::JBUTTON_13, StellaEvent::JBUTTON_14, - StellaEvent::JBUTTON_15, StellaEvent::JBUTTON_16, StellaEvent::JBUTTON_17, - StellaEvent::JBUTTON_18, StellaEvent::JBUTTON_19 - }; - - enum JoyType { JT_NONE, JT_REGULAR, JT_STELLADAPTOR_1, JT_STELLADAPTOR_2 }; - - struct Stella_Joystick - { - SDL_Joystick* stick; - JoyType type; - }; - - static Stella_Joystick theJoysticks[StellaEvent::LastJSTICK]; - - // Static lookup tables for Stelladaptor axis support - static Event::Type SA_Axis[2][2][3] = { - Event::JoystickZeroLeft, Event::JoystickZeroRight, Event::PaddleZeroResistance, - Event::JoystickZeroUp, Event::JoystickZeroDown, Event::PaddleOneResistance, - Event::JoystickOneLeft, Event::JoystickOneRight, Event::PaddleTwoResistance, - Event::JoystickOneUp, Event::JoystickOneDown, Event::PaddleThreeResistance - }; - static Event::Type SA_DrivingValue[2] = { - Event::DrivingZeroValue, Event::DrivingOneValue - }; -#endif - -// Pointer to the console object or the null pointer -static Console* theConsole = (Console*) NULL; - -// Pointer to the display object or the null pointer -static FrameBufferSDL* theDisplay = (FrameBufferSDL*) NULL; - -// Pointer to the sound object or the null pointer -static Sound* theSound = (Sound*) NULL; - -// Pointer to the settings object or the null pointer -static Settings* theSettings = (Settings*) NULL; - -// Indicates if the mouse should be grabbed -static bool theGrabMouseIndicator = false; - -// Indicates if the mouse cursor should be hidden -static bool theHideCursorIndicator = false; - -// Indicates the current paddle mode -static Int32 thePaddleMode; - -// Indicates relative mouse position horizontally -static Int32 mouseX = 0; - -// Indicates whether to show information during program execution -static bool theShowInfoFlag; - -struct Switches -{ - SDLKey scanCode; - StellaEvent::KeyCode keyCode; -}; - -// Place the most used keys first to speed up access -// Todo - initialize this array in the same order as the SDLK -// keys are defined, so it can be a constant-time LUT -static Switches keyList[] = { - { SDLK_F1, StellaEvent::KCODE_F1 }, - { SDLK_F2, StellaEvent::KCODE_F2 }, - { SDLK_F3, StellaEvent::KCODE_F3 }, - { SDLK_F4, StellaEvent::KCODE_F4 }, - { SDLK_F5, StellaEvent::KCODE_F5 }, - { SDLK_F6, StellaEvent::KCODE_F6 }, - { SDLK_F7, StellaEvent::KCODE_F7 }, - { SDLK_F8, StellaEvent::KCODE_F8 }, - { SDLK_F9, StellaEvent::KCODE_F9 }, - { SDLK_F10, StellaEvent::KCODE_F10 }, - { SDLK_F11, StellaEvent::KCODE_F11 }, - { SDLK_F12, StellaEvent::KCODE_F12 }, - { SDLK_F13, StellaEvent::KCODE_F13 }, - { SDLK_F14, StellaEvent::KCODE_F14 }, - { SDLK_F15, StellaEvent::KCODE_F15 }, - - { SDLK_UP, StellaEvent::KCODE_UP }, - { SDLK_DOWN, StellaEvent::KCODE_DOWN }, - { SDLK_LEFT, StellaEvent::KCODE_LEFT }, - { SDLK_RIGHT, StellaEvent::KCODE_RIGHT }, - { SDLK_SPACE, StellaEvent::KCODE_SPACE }, - { SDLK_LCTRL, StellaEvent::KCODE_LCTRL }, - { SDLK_RCTRL, StellaEvent::KCODE_RCTRL }, - { SDLK_LALT, StellaEvent::KCODE_LALT }, - { SDLK_RALT, StellaEvent::KCODE_RALT }, - { SDLK_LSUPER, StellaEvent::KCODE_LWIN }, - { SDLK_RSUPER, StellaEvent::KCODE_RWIN }, - { SDLK_MENU, StellaEvent::KCODE_MENU }, - - { SDLK_a, StellaEvent::KCODE_a }, - { SDLK_b, StellaEvent::KCODE_b }, - { SDLK_c, StellaEvent::KCODE_c }, - { SDLK_d, StellaEvent::KCODE_d }, - { SDLK_e, StellaEvent::KCODE_e }, - { SDLK_f, StellaEvent::KCODE_f }, - { SDLK_g, StellaEvent::KCODE_g }, - { SDLK_h, StellaEvent::KCODE_h }, - { SDLK_i, StellaEvent::KCODE_i }, - { SDLK_j, StellaEvent::KCODE_j }, - { SDLK_k, StellaEvent::KCODE_k }, - { SDLK_l, StellaEvent::KCODE_l }, - { SDLK_m, StellaEvent::KCODE_m }, - { SDLK_n, StellaEvent::KCODE_n }, - { SDLK_o, StellaEvent::KCODE_o }, - { SDLK_p, StellaEvent::KCODE_p }, - { SDLK_q, StellaEvent::KCODE_q }, - { SDLK_r, StellaEvent::KCODE_r }, - { SDLK_s, StellaEvent::KCODE_s }, - { SDLK_t, StellaEvent::KCODE_t }, - { SDLK_u, StellaEvent::KCODE_u }, - { SDLK_v, StellaEvent::KCODE_v }, - { SDLK_w, StellaEvent::KCODE_w }, - { SDLK_x, StellaEvent::KCODE_x }, - { SDLK_y, StellaEvent::KCODE_y }, - { SDLK_z, StellaEvent::KCODE_z }, - - { SDLK_0, StellaEvent::KCODE_0 }, - { SDLK_1, StellaEvent::KCODE_1 }, - { SDLK_2, StellaEvent::KCODE_2 }, - { SDLK_3, StellaEvent::KCODE_3 }, - { SDLK_4, StellaEvent::KCODE_4 }, - { SDLK_5, StellaEvent::KCODE_5 }, - { SDLK_6, StellaEvent::KCODE_6 }, - { SDLK_7, StellaEvent::KCODE_7 }, - { SDLK_8, StellaEvent::KCODE_8 }, - { SDLK_9, StellaEvent::KCODE_9 }, - - { SDLK_KP0, StellaEvent::KCODE_KP0 }, - { SDLK_KP1, StellaEvent::KCODE_KP1 }, - { SDLK_KP2, StellaEvent::KCODE_KP2 }, - { SDLK_KP3, StellaEvent::KCODE_KP3 }, - { SDLK_KP4, StellaEvent::KCODE_KP4 }, - { SDLK_KP5, StellaEvent::KCODE_KP5 }, - { SDLK_KP6, StellaEvent::KCODE_KP6 }, - { SDLK_KP7, StellaEvent::KCODE_KP7 }, - { SDLK_KP8, StellaEvent::KCODE_KP8 }, - { SDLK_KP9, StellaEvent::KCODE_KP9 }, - { SDLK_KP_PERIOD, StellaEvent::KCODE_KP_PERIOD }, - { SDLK_KP_DIVIDE, StellaEvent::KCODE_KP_DIVIDE }, - { SDLK_KP_MULTIPLY, StellaEvent::KCODE_KP_MULTIPLY}, - { SDLK_KP_MINUS, StellaEvent::KCODE_KP_MINUS }, - { SDLK_KP_PLUS, StellaEvent::KCODE_KP_PLUS }, - { SDLK_KP_ENTER, StellaEvent::KCODE_KP_ENTER }, - { SDLK_KP_EQUALS, StellaEvent::KCODE_KP_EQUALS }, - - { SDLK_BACKSPACE, StellaEvent::KCODE_BACKSPACE }, - { SDLK_TAB, StellaEvent::KCODE_TAB }, - { SDLK_CLEAR, StellaEvent::KCODE_CLEAR }, - { SDLK_RETURN, StellaEvent::KCODE_RETURN }, - { SDLK_ESCAPE, StellaEvent::KCODE_ESCAPE }, - { SDLK_COMMA, StellaEvent::KCODE_COMMA }, - { SDLK_MINUS, StellaEvent::KCODE_MINUS }, - { SDLK_PERIOD, StellaEvent::KCODE_PERIOD }, - { SDLK_SLASH, StellaEvent::KCODE_SLASH }, - { SDLK_BACKSLASH, StellaEvent::KCODE_BACKSLASH }, - { SDLK_SEMICOLON, StellaEvent::KCODE_SEMICOLON }, - { SDLK_EQUALS, StellaEvent::KCODE_EQUALS }, - { SDLK_QUOTE, StellaEvent::KCODE_QUOTE }, - { SDLK_BACKQUOTE, StellaEvent::KCODE_BACKQUOTE }, - { SDLK_LEFTBRACKET, StellaEvent::KCODE_LEFTBRACKET}, - { SDLK_RIGHTBRACKET,StellaEvent::KCODE_RIGHTBRACKET}, - - { SDLK_PRINT, StellaEvent::KCODE_PRTSCREEN }, - { SDLK_MODE, StellaEvent::KCODE_SCRLOCK }, - { SDLK_PAUSE, StellaEvent::KCODE_PAUSE }, - { SDLK_INSERT, StellaEvent::KCODE_INSERT }, - { SDLK_HOME, StellaEvent::KCODE_HOME }, - { SDLK_PAGEUP, StellaEvent::KCODE_PAGEUP }, - { SDLK_DELETE, StellaEvent::KCODE_DELETE }, - { SDLK_END, StellaEvent::KCODE_END }, - { SDLK_PAGEDOWN, StellaEvent::KCODE_PAGEDOWN } - }; - -/** - Returns number of ticks in microseconds -*/ -#ifdef HAVE_GETTIMEOFDAY -inline uInt32 getTicks() -{ - timeval now; - gettimeofday(&now, 0); - - return (uInt32) (now.tv_sec * 1000000 + now.tv_usec); -} -#else -inline uInt32 getTicks() -{ - return (uInt32) SDL_GetTicks() * 1000; -} -#endif - - -/** - This routine should be called once setupDisplay is called - to create the joystick stuff. -*/ -bool setupJoystick() -{ -#ifdef JOYSTICK_SUPPORT - // Keep track of how many Stelladaptors we've found - uInt8 saCount = 0; - - // First clear the joystick array - for(uInt32 i = 0; i < StellaEvent::LastJSTICK; i++) - { - theJoysticks[i].stick = (SDL_Joystick*) NULL; - theJoysticks[i].type = JT_NONE; - } - - // Initialize the joystick subsystem - if((SDL_InitSubSystem(SDL_INIT_JOYSTICK) == -1) || (SDL_NumJoysticks() <= 0)) - { - if(theShowInfoFlag) - cout << "No joysticks present, use the keyboard.\n"; - - return true; - } - - // Try to open 4 regular joysticks and 2 Stelladaptor devices - uInt32 limit = SDL_NumJoysticks() <= StellaEvent::LastJSTICK ? - SDL_NumJoysticks() : StellaEvent::LastJSTICK; - for(uInt32 i = 0; i < limit; i++) - { - string name = SDL_JoystickName(i); - theJoysticks[i].stick = SDL_JoystickOpen(i); - - // Skip if we couldn't open it for any reason - if(theJoysticks[i].stick == NULL) - continue; - - // Figure out what type of joystick this is - if(name.find("Stelladaptor", 0) != string::npos) - { - saCount++; - if(saCount > 2) // Ignore more than 2 Stelladaptors - { - theJoysticks[i].type = JT_NONE; - continue; - } - else if(saCount == 1) - { - name = "Left Stelladaptor (Left joystick, Paddles 0 and 1, Left driving controller)"; - theJoysticks[i].type = JT_STELLADAPTOR_1; - } - else if(saCount == 2) - { - name = "Right Stelladaptor (Right joystick, Paddles 2 and 3, Right driving controller)"; - theJoysticks[i].type = JT_STELLADAPTOR_2; - } - - if(theShowInfoFlag) - cout << "Joystick " << i << ": " << name << endl; - } - else - { - theJoysticks[i].type = JT_REGULAR; - if(theShowInfoFlag) - { - cout << "Joystick " << i << ": " << SDL_JoystickName(i) - << " with " << SDL_JoystickNumButtons(theJoysticks[i].stick) - << " buttons.\n"; - } - } - } -#endif - - return true; -} - - -/** - This routine should be called regularly to handle events -*/ -void handleEvents() -{ - SDL_Event event; - Uint8 type; - SDLKey key; - SDLMod mod; - - // Check for an event - while(SDL_PollEvent(&event)) - { - // keyboard events - if(event.type == SDL_KEYDOWN) - { - key = event.key.keysym.sym; - mod = event.key.keysym.mod; - type = event.type; - - // An attempt to speed up event processing - // All SDL-specific event actions are accessed by either - // Control or Alt keys. So we quickly check for those. - if(mod & KMOD_ALT) - { - if(key == SDLK_EQUALS) - theDisplay->resize(1); - else if(key == SDLK_MINUS) - theDisplay->resize(-1); - else if(key == SDLK_RETURN) - { - theDisplay->toggleFullscreen(); - } -#ifdef DISPLAY_OPENGL - else if(key == SDLK_f && theUseOpenGLFlag) - ((FrameBufferGL*)theDisplay)->toggleFilter(); -#endif -#ifdef DEVELOPER_SUPPORT - else if(key == SDLK_END) // Alt-End increases XStart - { - theConsole->changeXStart(1); - theDisplay->resize(0); - } - else if(key == SDLK_HOME) // Alt-Home decreases XStart - { - theConsole->changeXStart(0); - theDisplay->resize(0); - } - else if(key == SDLK_PAGEUP) // Alt-PageUp increases YStart - { - theConsole->changeYStart(1); - theDisplay->resize(0); - } - else if(key == SDLK_PAGEDOWN) // Alt-PageDown decreases YStart - { - theConsole->changeYStart(0); - theDisplay->resize(0); - } -#endif - } - else if(mod & KMOD_CTRL) - { - if(key == SDLK_g) - { - // don't change grabmouse in fullscreen mode - if(!theDisplay->fullScreen()) - { - theGrabMouseIndicator = !theGrabMouseIndicator; - theSettings->setBool("grabmouse", theGrabMouseIndicator); - theDisplay->grabMouse(theGrabMouseIndicator); - } - } - else if(key == SDLK_h) - { - // don't change hidecursor in fullscreen mode - if(!theDisplay->fullScreen()) - { - theHideCursorIndicator = !theHideCursorIndicator; - theSettings->setBool("hidecursor", theHideCursorIndicator); - theDisplay->showCursor(!theHideCursorIndicator); - } - } -#ifdef DEVELOPER_SUPPORT - else if(key == SDLK_f) // Ctrl-f toggles NTSC/PAL mode - { - theConsole->toggleFormat(); - theDisplay->setupPalette(); - } - else if(key == SDLK_p) // Ctrl-p toggles different palettes - { - theConsole->togglePalette(); - theDisplay->setupPalette(); - } - else if(key == SDLK_END) // Ctrl-End increases Width - { - theConsole->changeWidth(1); - theDisplay->resize(0); - } - else if(key == SDLK_HOME) // Ctrl-Home decreases Width - { - theConsole->changeWidth(0); - theDisplay->resize(0); - } - else if(key == SDLK_PAGEUP) // Ctrl-PageUp increases Height - { - theConsole->changeHeight(1); - theDisplay->resize(0); - } - else if(key == SDLK_PAGEDOWN) // Ctrl-PageDown decreases Height - { - theConsole->changeHeight(0); - theDisplay->resize(0); - } - else if(key == SDLK_s) // Ctrl-s saves properties to a file - { - if(theConsole->settings().getBool("mergeprops")) // Attempt to merge with propertiesSet - { - theConsole->saveProperties(theSettings->userPropertiesFilename(), true); - } - else // Save to file in home directory - { - string newPropertiesFile = theConsole->settings().baseDir() + "/" + \ - theConsole->properties().get("Cartridge.Name") + ".pro"; - replace(newPropertiesFile.begin(), newPropertiesFile.end(), ' ', '_'); - theConsole->saveProperties(newPropertiesFile); - } - } -#endif - } - else // check all the other keys - { - for(unsigned int i = 0; i < sizeof(keyList) / sizeof(Switches); ++i) - { - if(keyList[i].scanCode == key) - theConsole->eventHandler().sendKeyEvent(keyList[i].keyCode, 1); - } - } - } - else if(event.type == SDL_KEYUP) - { - key = event.key.keysym.sym; - type = event.type; - - for(unsigned int i = 0; i < sizeof(keyList) / sizeof(Switches); ++i) - { - if(keyList[i].scanCode == key) - theConsole->eventHandler().sendKeyEvent(keyList[i].keyCode, 0); - } - } - else if(event.type == SDL_MOUSEMOTION) - { - Int32 resistance; - uInt32 zoom = theDisplay->zoomLevel(); - Int32 width = theDisplay->width() * zoom; - Event::Type type = Event::NoType; - - // Grabmouse and hidecursor introduce some lag into the mouse movement, - // so we need to fudge the numbers a bit - if(theGrabMouseIndicator && theHideCursorIndicator) - { - mouseX = (int)((float)mouseX + (float)event.motion.xrel - * 1.5 * (float) zoom); - } - else - { - mouseX = mouseX + event.motion.xrel * zoom; - } - - // Check to make sure mouseX is within the game window - if(mouseX < 0) - mouseX = 0; - else if(mouseX > width) - mouseX = width; - - resistance = (Int32)(1000000.0 * (width - mouseX) / width); - - // Now, set the event of the correct paddle to the calculated resistance - if(thePaddleMode == 0) - type = Event::PaddleZeroResistance; - else if(thePaddleMode == 1) - type = Event::PaddleOneResistance; - else if(thePaddleMode == 2) - type = Event::PaddleTwoResistance; - else if(thePaddleMode == 3) - type = Event::PaddleThreeResistance; - - theConsole->eventHandler().sendEvent(type, resistance); - } - else if(event.type == SDL_MOUSEBUTTONDOWN || event.type == SDL_MOUSEBUTTONUP) - { - Event::Type type = Event::LastType; - Int32 value; - - if(event.type == SDL_MOUSEBUTTONDOWN) - value = 1; - else - value = 0; - - if(thePaddleMode == 0) - type = Event::PaddleZeroFire; - else if(thePaddleMode == 1) - type = Event::PaddleOneFire; - else if(thePaddleMode == 2) - type = Event::PaddleTwoFire; - else if(thePaddleMode == 3) - type = Event::PaddleThreeFire; - - theConsole->eventHandler().sendEvent(type, value); - } - else if(event.type == SDL_ACTIVEEVENT) - { - if((event.active.state & SDL_APPACTIVE) && (event.active.gain == 0)) - { - if(!theConsole->eventHandler().doPause()) - { - theConsole->eventHandler().sendEvent(Event::Pause, 1); - } - } - } - else if(event.type == SDL_QUIT) - { - theConsole->eventHandler().sendEvent(Event::Quit, 1); - } - else if(event.type == SDL_VIDEOEXPOSE) - { - theDisplay->refresh(); - } - -#ifdef JOYSTICK_SUPPORT - // Read joystick events and modify event states - StellaEvent::JoyStick stick; - StellaEvent::JoyCode code; - Int32 state; - Uint8 axis; - Uint8 button; - Int32 resistance; - Sint16 value; - JoyType type; - - if(event.jbutton.which >= StellaEvent::LastJSTICK) - return; - - stick = joyList[event.jbutton.which]; - type = theJoysticks[event.jbutton.which].type; - - // Figure put what type of joystick we're dealing with - // Stelladaptors behave differently, and can't be remapped - switch(type) - { - case JT_NONE: - break; - - case JT_REGULAR: - - if((event.type == SDL_JOYBUTTONDOWN) || (event.type == SDL_JOYBUTTONUP)) - { - if(event.jbutton.button >= StellaEvent::LastJCODE) - return; - - code = joyButtonList[event.jbutton.button]; - state = event.jbutton.state == SDL_PRESSED ? 1 : 0; - - theConsole->eventHandler().sendJoyEvent(stick, code, state); - } - else if(event.type == SDL_JOYAXISMOTION) - { - axis = event.jaxis.axis; - value = event.jaxis.value; - - if(axis == 0) // x-axis - { - theConsole->eventHandler().sendJoyEvent(stick, StellaEvent::JAXIS_LEFT, - (value < -16384) ? 1 : 0); - theConsole->eventHandler().sendJoyEvent(stick, StellaEvent::JAXIS_RIGHT, - (value > 16384) ? 1 : 0); - } - else if(axis == 1) // y-axis - { - theConsole->eventHandler().sendJoyEvent(stick, StellaEvent::JAXIS_UP, - (value < -16384) ? 1 : 0); - theConsole->eventHandler().sendJoyEvent(stick, StellaEvent::JAXIS_DOWN, - (value > 16384) ? 1 : 0); - } - } - break; // Regular joystick - - case JT_STELLADAPTOR_1: - case JT_STELLADAPTOR_2: - - if((event.type == SDL_JOYBUTTONDOWN) || (event.type == SDL_JOYBUTTONUP)) - { - button = event.jbutton.button; - state = event.jbutton.state == SDL_PRESSED ? 1 : 0; - - // Send button events for the joysticks/paddles - if(button == 0) - { - if(type == JT_STELLADAPTOR_1) - { - theConsole->eventHandler().sendEvent(Event::JoystickZeroFire, state); - theConsole->eventHandler().sendEvent(Event::DrivingZeroFire, state); - theConsole->eventHandler().sendEvent(Event::PaddleZeroFire, state); - } - else - { - theConsole->eventHandler().sendEvent(Event::JoystickOneFire, state); - theConsole->eventHandler().sendEvent(Event::DrivingOneFire, state); - theConsole->eventHandler().sendEvent(Event::PaddleTwoFire, state); - } - } - else if(button == 1) - { - if(type == JT_STELLADAPTOR_1) - theConsole->eventHandler().sendEvent(Event::PaddleOneFire, state); - else - theConsole->eventHandler().sendEvent(Event::PaddleThreeFire, state); - } - } - else if(event.type == SDL_JOYAXISMOTION) - { - axis = event.jaxis.axis; - value = event.jaxis.value; - - // Send axis events for the joysticks - theConsole->eventHandler().sendEvent(SA_Axis[type-2][axis][0], - (value < -16384) ? 1 : 0); - theConsole->eventHandler().sendEvent(SA_Axis[type-2][axis][1], - (value > 16384) ? 1 : 0); - - // Send axis events for the paddles - resistance = (Int32) (1000000.0 * (32767 - value) / 65534); - theConsole->eventHandler().sendEvent(SA_Axis[type-2][axis][2], resistance); - - // Send events for the driving controllers - if(axis == 1) - { - if(value <= -16384-4096) - theConsole->eventHandler().sendEvent(SA_DrivingValue[type-2],2); - else if(value > 16384+4096) - theConsole->eventHandler().sendEvent(SA_DrivingValue[type-2],1); - else if(value >= 16384-4096) - theConsole->eventHandler().sendEvent(SA_DrivingValue[type-2],0); - else - theConsole->eventHandler().sendEvent(SA_DrivingValue[type-2],3); - } - } - break; // Stelladaptor joystick - - default: - break; - } -#endif - } -} - - -/** - Setup the properties set by first checking for a user file - "$HOME/.stella/stella.pro", then a system-wide file "/etc/stella.pro". - Return false if neither file is found, else return true. - - @param set The properties set to setup -*/ -bool setupProperties(PropertiesSet& set) -{ - bool useMemList = false; - string theAlternateProFile = theSettings->getString("altpro"); - string theUserProFile = theSettings->userPropertiesFilename(); - string theSystemProFile = theSettings->systemPropertiesFilename(); - -#ifdef DEVELOPER_SUPPORT - // If the user wishes to merge any property modifications to the - // PropertiesSet file, then the PropertiesSet file MUST be loaded - // into memory. - useMemList = theSettings->getBool("mergeprops"); -#endif - - // Check to see if the user has specified an alternate .pro file. - - if(theAlternateProFile != "") - { - set.load(theAlternateProFile, &Console::defaultProperties(), useMemList); - return true; - } - - if(theUserProFile != "") - { - set.load(theUserProFile, &Console::defaultProperties(), useMemList); - return true; - } - else if(theSystemProFile != "") - { - set.load(theSystemProFile, &Console::defaultProperties(), useMemList); - return true; - } - else - { - set.load("", &Console::defaultProperties(), false); - return true; - } -} - - -/** - Does general cleanup in case any operation failed (or at end of program). -*/ -void cleanup() -{ -#ifdef JOYSTICK_SUPPORT - if(SDL_WasInit(SDL_INIT_JOYSTICK) & SDL_INIT_JOYSTICK) - { - for(uInt32 i = 0; i < StellaEvent::LastJSTICK; i++) - { - if(SDL_JoystickOpened(i)) - SDL_JoystickClose(theJoysticks[i].stick); - } - } -#endif - - if(theSettings) - delete theSettings; - - if(theConsole) - delete theConsole; - - if(theSound) - delete theSound; - - if(theDisplay) - delete theDisplay; - - if(SDL_WasInit(SDL_INIT_VIDEO) & SDL_INIT_VIDEO) - SDL_Quit(); -} - - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -int main(int argc, char* argv[]) -{ -#if defined(UNIX) - theSettings = new SettingsUNIX(); -#elif defined(WIN32) - theSettings = new SettingsWin32(); -#else - #error Unsupported platform! -#endif - if(!theSettings) - { - cleanup(); - return 0; - } - theSettings->loadConfig(); - - // Take care of commandline arguments - if(!theSettings->loadCommandLine(argc, argv)) - { - string message = "Stella version 1.4_cvs\n\nUsage: stella [options ...] romfile"; - theSettings->usage(message); - cleanup(); - return 0; - } - - // Cache some settings so they don't have to be repeatedly searched for - thePaddleMode = theSettings->getInt("paddle"); - theShowInfoFlag = theSettings->getBool("showinfo"); - theGrabMouseIndicator = theSettings->getBool("grabmouse"); - theHideCursorIndicator = theSettings->getBool("hidecursor"); - - // Request that the SDL window be centered, if possible - putenv("SDL_VIDEO_CENTERED=1"); - - // Get a pointer to the file which contains the cartridge ROM - const char* file = argv[argc - 1]; - - // Open the cartridge image and read it in - ifstream in(file, ios_base::binary); - if(!in) - { - cerr << "ERROR: Couldn't open " << file << "..." << endl; - string message = "Stella version 1.4_cvs\n\nUsage: stella [options ...] romfile"; - theSettings->usage(message); - cleanup(); - return 0; - } - - uInt8* image = new uInt8[512 * 1024]; - in.read((char*)image, 512 * 1024); - uInt32 size = in.gcount(); - in.close(); - - // Create a properties set for us to use and set it up - PropertiesSet propertiesSet; - if(!setupProperties(propertiesSet)) - { - delete[] image; - cleanup(); - return 0; - } - - // Create an SDL window - string videodriver = theSettings->getString("video"); - if(videodriver == "soft") - { - theDisplay = new FrameBufferSoft(); - if(theShowInfoFlag) - cout << "Using software mode for video.\n"; - } -#ifdef DISPLAY_OPENGL - else if(videodriver == "gl") - { - theDisplay = new FrameBufferGL(); - theUseOpenGLFlag = true; - if(theShowInfoFlag) - cout << "Using OpenGL mode for video.\n"; - } -#endif - else // a driver that doesn't exist was requested, so use software mode - { - theDisplay = new FrameBufferSoft(); - if(theShowInfoFlag) - cout << "Using software mode for video.\n"; - } - - if(!theDisplay) - { - cerr << "ERROR: Couldn't set up display.\n"; - delete[] image; - cleanup(); - return 0; - } - - // Create a sound object for playing audio - if(theSettings->getBool("sound")) - { - uInt32 fragsize = theSettings->getInt("fragsize"); - theSound = new SoundSDL(fragsize); - if(theShowInfoFlag) - { - cout << "Sound enabled, using fragment size = " << fragsize; - cout << "." << endl; - } - } - else // even if sound has been disabled, we still need a sound object - { - theSound = new Sound(); - if(theShowInfoFlag) - cout << "Sound disabled.\n"; - } - theSound->setVolume(theSettings->getInt("volume")); - - // Get just the filename of the file containing the ROM image - const char* filename = (!strrchr(file, '/')) ? file : strrchr(file, '/') + 1; - - // Create the 2600 game console - theConsole = new Console(image, size, filename, *theSettings, propertiesSet, - *theDisplay, *theSound); - - // Free the image since we don't need it any longer - delete[] image; - - // Setup the SDL joysticks - // This must be done after the console is created - if(!setupJoystick()) - { - cerr << "ERROR: Couldn't set up joysticks.\n"; - cleanup(); - return 0; - } - - // These variables are common to both timing options - // and are needed to calculate the overall frames per second. - uInt32 frameTime = 0, numberOfFrames = 0; - - if(theSettings->getBool("accurate")) // normal, CPU-intensive timing - { - // Set up accurate timing stuff - uInt32 startTime, delta; - uInt32 timePerFrame = (uInt32)(1000000.0 / (double)theSettings->getInt("framerate")); - - // Set the base for the timers - frameTime = 0; - - // Main game loop - for(;;) - { - // Exit if the user wants to quit - if(theConsole->eventHandler().doQuit()) - { - break; - } - - startTime = getTicks(); - handleEvents(); - theConsole->update(); - - // Now, waste time if we need to so that we are at the desired frame rate - for(;;) - { - delta = getTicks() - startTime; - - if(delta >= timePerFrame) - break; - } - - frameTime += getTicks() - startTime; - ++numberOfFrames; - } - } - else // less accurate, less CPU-intensive timing - { - // Set up less accurate timing stuff - uInt32 startTime, virtualTime, currentTime; - uInt32 timePerFrame = (uInt32)(1000000.0 / (double)theSettings->getInt("framerate")); - - // Set the base for the timers - virtualTime = getTicks(); - frameTime = 0; - - // Main game loop - for(;;) - { - // Exit if the user wants to quit - if(theConsole->eventHandler().doQuit()) - { - break; - } - - startTime = getTicks(); - handleEvents(); - theConsole->update(); - - currentTime = getTicks(); - virtualTime += timePerFrame; - if(currentTime < virtualTime) - { - SDL_Delay((virtualTime - currentTime)/1000); - } - - currentTime = getTicks() - startTime; - frameTime += currentTime; - ++numberOfFrames; - } - } - - if(theShowInfoFlag) - { - double executionTime = (double) frameTime / 1000000.0; - double framesPerSecond = (double) numberOfFrames / executionTime; - - cout << endl; - cout << numberOfFrames << " total frames drawn\n"; - cout << framesPerSecond << " frames/second\n"; - cout << endl; - cout << "Cartridge Name: " << theConsole->properties().get("Cartridge.Name"); - cout << endl; - cout << "Cartridge MD5: " << theConsole->properties().get("Cartridge.MD5"); - cout << endl << endl; - } - - // Cleanup time ... - cleanup(); - return 0; -} +//============================================================================ +// +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa +// SSSS tt ee ee ll ll aa +// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" +// SS SS tt ee ll ll aa aa +// SSSS ttt eeeee llll llll aaaaa +// +// Copyright (c) 1995-2004 by Bradford W. Mott +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: mainSDL.cxx,v 1.6 2004-06-16 00:31:32 stephena Exp $ +//============================================================================ + +#include +#include +#include +#include +#include + +#ifdef HAVE_GETTIMEOFDAY + #include + #include +#endif + +#include + +#include "bspf.hxx" +#include "Console.hxx" +#include "Event.hxx" +#include "StellaEvent.hxx" +#include "EventHandler.hxx" +#include "FrameBuffer.hxx" +#include "FrameBufferSDL.hxx" +#include "FrameBufferSoft.hxx" +#include "PropsSet.hxx" +#include "Sound.hxx" +#include "SoundSDL.hxx" +#include "Settings.hxx" + +#ifdef DISPLAY_OPENGL + #include "FrameBufferGL.hxx" + + // Indicates whether to use OpenGL mode + static bool theUseOpenGLFlag; +#endif + +#if defined(UNIX) + #include "SettingsUNIX.hxx" +#elif defined(WIN32) + #include "SettingsWin32.hxx" +#else + #error Unsupported platform! +#endif + +static void cleanup(); +static bool setupJoystick(); +static void handleEvents(); +static uInt32 getTicks(); +static bool setupProperties(PropertiesSet& set); + +#ifdef JOYSTICK_SUPPORT +// static uInt32 thePaddleNumber; + + // Lookup table for joystick numbers and events + StellaEvent::JoyStick joyList[StellaEvent::LastJSTICK] = { + StellaEvent::JSTICK_0, StellaEvent::JSTICK_1, StellaEvent::JSTICK_2, + StellaEvent::JSTICK_3, StellaEvent::JSTICK_5, StellaEvent::JSTICK_5 + }; + StellaEvent::JoyCode joyButtonList[StellaEvent::LastJCODE] = { + StellaEvent::JBUTTON_0, StellaEvent::JBUTTON_1, StellaEvent::JBUTTON_2, + StellaEvent::JBUTTON_3, StellaEvent::JBUTTON_4, StellaEvent::JBUTTON_5, + StellaEvent::JBUTTON_6, StellaEvent::JBUTTON_7, StellaEvent::JBUTTON_8, + StellaEvent::JBUTTON_9, StellaEvent::JBUTTON_10, StellaEvent::JBUTTON_11, + StellaEvent::JBUTTON_12, StellaEvent::JBUTTON_13, StellaEvent::JBUTTON_14, + StellaEvent::JBUTTON_15, StellaEvent::JBUTTON_16, StellaEvent::JBUTTON_17, + StellaEvent::JBUTTON_18, StellaEvent::JBUTTON_19 + }; + + enum JoyType { JT_NONE, JT_REGULAR, JT_STELLADAPTOR_1, JT_STELLADAPTOR_2 }; + + struct Stella_Joystick + { + SDL_Joystick* stick; + JoyType type; + }; + + static Stella_Joystick theJoysticks[StellaEvent::LastJSTICK]; + + // Static lookup tables for Stelladaptor axis support + static Event::Type SA_Axis[2][2][3] = { + Event::JoystickZeroLeft, Event::JoystickZeroRight, Event::PaddleZeroResistance, + Event::JoystickZeroUp, Event::JoystickZeroDown, Event::PaddleOneResistance, + Event::JoystickOneLeft, Event::JoystickOneRight, Event::PaddleTwoResistance, + Event::JoystickOneUp, Event::JoystickOneDown, Event::PaddleThreeResistance + }; + static Event::Type SA_DrivingValue[2] = { + Event::DrivingZeroValue, Event::DrivingOneValue + }; +#endif + +// Pointer to the console object or the null pointer +static Console* theConsole = (Console*) NULL; + +// Pointer to the display object or the null pointer +static FrameBufferSDL* theDisplay = (FrameBufferSDL*) NULL; + +// Pointer to the sound object or the null pointer +static Sound* theSound = (Sound*) NULL; + +// Pointer to the settings object or the null pointer +static Settings* theSettings = (Settings*) NULL; + +// Indicates if the mouse should be grabbed +static bool theGrabMouseIndicator = false; + +// Indicates if the mouse cursor should be hidden +static bool theHideCursorIndicator = false; + +// Indicates the current paddle mode +static Int32 thePaddleMode; + +// Indicates relative mouse position horizontally +static Int32 mouseX = 0; + +// Indicates whether to show information during program execution +static bool theShowInfoFlag; + +struct Switches +{ + SDLKey scanCode; + StellaEvent::KeyCode keyCode; +}; + +// Place the most used keys first to speed up access +// Todo - initialize this array in the same order as the SDLK +// keys are defined, so it can be a constant-time LUT +static Switches keyList[] = { + { SDLK_F1, StellaEvent::KCODE_F1 }, + { SDLK_F2, StellaEvent::KCODE_F2 }, + { SDLK_F3, StellaEvent::KCODE_F3 }, + { SDLK_F4, StellaEvent::KCODE_F4 }, + { SDLK_F5, StellaEvent::KCODE_F5 }, + { SDLK_F6, StellaEvent::KCODE_F6 }, + { SDLK_F7, StellaEvent::KCODE_F7 }, + { SDLK_F8, StellaEvent::KCODE_F8 }, + { SDLK_F9, StellaEvent::KCODE_F9 }, + { SDLK_F10, StellaEvent::KCODE_F10 }, + { SDLK_F11, StellaEvent::KCODE_F11 }, + { SDLK_F12, StellaEvent::KCODE_F12 }, + { SDLK_F13, StellaEvent::KCODE_F13 }, + { SDLK_F14, StellaEvent::KCODE_F14 }, + { SDLK_F15, StellaEvent::KCODE_F15 }, + + { SDLK_UP, StellaEvent::KCODE_UP }, + { SDLK_DOWN, StellaEvent::KCODE_DOWN }, + { SDLK_LEFT, StellaEvent::KCODE_LEFT }, + { SDLK_RIGHT, StellaEvent::KCODE_RIGHT }, + { SDLK_SPACE, StellaEvent::KCODE_SPACE }, + { SDLK_LCTRL, StellaEvent::KCODE_LCTRL }, + { SDLK_RCTRL, StellaEvent::KCODE_RCTRL }, + { SDLK_LALT, StellaEvent::KCODE_LALT }, + { SDLK_RALT, StellaEvent::KCODE_RALT }, + { SDLK_LSUPER, StellaEvent::KCODE_LWIN }, + { SDLK_RSUPER, StellaEvent::KCODE_RWIN }, + { SDLK_MENU, StellaEvent::KCODE_MENU }, + + { SDLK_a, StellaEvent::KCODE_a }, + { SDLK_b, StellaEvent::KCODE_b }, + { SDLK_c, StellaEvent::KCODE_c }, + { SDLK_d, StellaEvent::KCODE_d }, + { SDLK_e, StellaEvent::KCODE_e }, + { SDLK_f, StellaEvent::KCODE_f }, + { SDLK_g, StellaEvent::KCODE_g }, + { SDLK_h, StellaEvent::KCODE_h }, + { SDLK_i, StellaEvent::KCODE_i }, + { SDLK_j, StellaEvent::KCODE_j }, + { SDLK_k, StellaEvent::KCODE_k }, + { SDLK_l, StellaEvent::KCODE_l }, + { SDLK_m, StellaEvent::KCODE_m }, + { SDLK_n, StellaEvent::KCODE_n }, + { SDLK_o, StellaEvent::KCODE_o }, + { SDLK_p, StellaEvent::KCODE_p }, + { SDLK_q, StellaEvent::KCODE_q }, + { SDLK_r, StellaEvent::KCODE_r }, + { SDLK_s, StellaEvent::KCODE_s }, + { SDLK_t, StellaEvent::KCODE_t }, + { SDLK_u, StellaEvent::KCODE_u }, + { SDLK_v, StellaEvent::KCODE_v }, + { SDLK_w, StellaEvent::KCODE_w }, + { SDLK_x, StellaEvent::KCODE_x }, + { SDLK_y, StellaEvent::KCODE_y }, + { SDLK_z, StellaEvent::KCODE_z }, + + { SDLK_0, StellaEvent::KCODE_0 }, + { SDLK_1, StellaEvent::KCODE_1 }, + { SDLK_2, StellaEvent::KCODE_2 }, + { SDLK_3, StellaEvent::KCODE_3 }, + { SDLK_4, StellaEvent::KCODE_4 }, + { SDLK_5, StellaEvent::KCODE_5 }, + { SDLK_6, StellaEvent::KCODE_6 }, + { SDLK_7, StellaEvent::KCODE_7 }, + { SDLK_8, StellaEvent::KCODE_8 }, + { SDLK_9, StellaEvent::KCODE_9 }, + + { SDLK_KP0, StellaEvent::KCODE_KP0 }, + { SDLK_KP1, StellaEvent::KCODE_KP1 }, + { SDLK_KP2, StellaEvent::KCODE_KP2 }, + { SDLK_KP3, StellaEvent::KCODE_KP3 }, + { SDLK_KP4, StellaEvent::KCODE_KP4 }, + { SDLK_KP5, StellaEvent::KCODE_KP5 }, + { SDLK_KP6, StellaEvent::KCODE_KP6 }, + { SDLK_KP7, StellaEvent::KCODE_KP7 }, + { SDLK_KP8, StellaEvent::KCODE_KP8 }, + { SDLK_KP9, StellaEvent::KCODE_KP9 }, + { SDLK_KP_PERIOD, StellaEvent::KCODE_KP_PERIOD }, + { SDLK_KP_DIVIDE, StellaEvent::KCODE_KP_DIVIDE }, + { SDLK_KP_MULTIPLY, StellaEvent::KCODE_KP_MULTIPLY}, + { SDLK_KP_MINUS, StellaEvent::KCODE_KP_MINUS }, + { SDLK_KP_PLUS, StellaEvent::KCODE_KP_PLUS }, + { SDLK_KP_ENTER, StellaEvent::KCODE_KP_ENTER }, + { SDLK_KP_EQUALS, StellaEvent::KCODE_KP_EQUALS }, + + { SDLK_BACKSPACE, StellaEvent::KCODE_BACKSPACE }, + { SDLK_TAB, StellaEvent::KCODE_TAB }, + { SDLK_CLEAR, StellaEvent::KCODE_CLEAR }, + { SDLK_RETURN, StellaEvent::KCODE_RETURN }, + { SDLK_ESCAPE, StellaEvent::KCODE_ESCAPE }, + { SDLK_COMMA, StellaEvent::KCODE_COMMA }, + { SDLK_MINUS, StellaEvent::KCODE_MINUS }, + { SDLK_PERIOD, StellaEvent::KCODE_PERIOD }, + { SDLK_SLASH, StellaEvent::KCODE_SLASH }, + { SDLK_BACKSLASH, StellaEvent::KCODE_BACKSLASH }, + { SDLK_SEMICOLON, StellaEvent::KCODE_SEMICOLON }, + { SDLK_EQUALS, StellaEvent::KCODE_EQUALS }, + { SDLK_QUOTE, StellaEvent::KCODE_QUOTE }, + { SDLK_BACKQUOTE, StellaEvent::KCODE_BACKQUOTE }, + { SDLK_LEFTBRACKET, StellaEvent::KCODE_LEFTBRACKET}, + { SDLK_RIGHTBRACKET,StellaEvent::KCODE_RIGHTBRACKET}, + + { SDLK_PRINT, StellaEvent::KCODE_PRTSCREEN }, + { SDLK_MODE, StellaEvent::KCODE_SCRLOCK }, + { SDLK_PAUSE, StellaEvent::KCODE_PAUSE }, + { SDLK_INSERT, StellaEvent::KCODE_INSERT }, + { SDLK_HOME, StellaEvent::KCODE_HOME }, + { SDLK_PAGEUP, StellaEvent::KCODE_PAGEUP }, + { SDLK_DELETE, StellaEvent::KCODE_DELETE }, + { SDLK_END, StellaEvent::KCODE_END }, + { SDLK_PAGEDOWN, StellaEvent::KCODE_PAGEDOWN } + }; + +/** + Returns number of ticks in microseconds +*/ +#ifdef HAVE_GETTIMEOFDAY +inline uInt32 getTicks() +{ + timeval now; + gettimeofday(&now, 0); + + return (uInt32) (now.tv_sec * 1000000 + now.tv_usec); +} +#else +inline uInt32 getTicks() +{ + return (uInt32) SDL_GetTicks() * 1000; +} +#endif + + +/** + This routine should be called once setupDisplay is called + to create the joystick stuff. +*/ +bool setupJoystick() +{ +#ifdef JOYSTICK_SUPPORT + // Keep track of how many Stelladaptors we've found + uInt8 saCount = 0; + + // First clear the joystick array + for(uInt32 i = 0; i < StellaEvent::LastJSTICK; i++) + { + theJoysticks[i].stick = (SDL_Joystick*) NULL; + theJoysticks[i].type = JT_NONE; + } + + // Initialize the joystick subsystem + if((SDL_InitSubSystem(SDL_INIT_JOYSTICK) == -1) || (SDL_NumJoysticks() <= 0)) + { + if(theShowInfoFlag) + cout << "No joysticks present, use the keyboard.\n"; + + return true; + } + + // Try to open 4 regular joysticks and 2 Stelladaptor devices + uInt32 limit = SDL_NumJoysticks() <= StellaEvent::LastJSTICK ? + SDL_NumJoysticks() : StellaEvent::LastJSTICK; + for(uInt32 i = 0; i < limit; i++) + { + string name = SDL_JoystickName(i); + theJoysticks[i].stick = SDL_JoystickOpen(i); + + // Skip if we couldn't open it for any reason + if(theJoysticks[i].stick == NULL) + continue; + + // Figure out what type of joystick this is + if(name.find("Stelladaptor", 0) != string::npos) + { + saCount++; + if(saCount > 2) // Ignore more than 2 Stelladaptors + { + theJoysticks[i].type = JT_NONE; + continue; + } + else if(saCount == 1) + { + name = "Left Stelladaptor (Left joystick, Paddles 0 and 1, Left driving controller)"; + theJoysticks[i].type = JT_STELLADAPTOR_1; + } + else if(saCount == 2) + { + name = "Right Stelladaptor (Right joystick, Paddles 2 and 3, Right driving controller)"; + theJoysticks[i].type = JT_STELLADAPTOR_2; + } + + if(theShowInfoFlag) + cout << "Joystick " << i << ": " << name << endl; + } + else + { + theJoysticks[i].type = JT_REGULAR; + if(theShowInfoFlag) + { + cout << "Joystick " << i << ": " << SDL_JoystickName(i) + << " with " << SDL_JoystickNumButtons(theJoysticks[i].stick) + << " buttons.\n"; + } + } + } +#endif + + return true; +} + + +/** + This routine should be called regularly to handle events +*/ +void handleEvents() +{ + SDL_Event event; + Uint8 type; + SDLKey key; + SDLMod mod; + + // Check for an event + while(SDL_PollEvent(&event)) + { + // keyboard events + if(event.type == SDL_KEYDOWN) + { + key = event.key.keysym.sym; + mod = event.key.keysym.mod; + type = event.type; + + // An attempt to speed up event processing + // All SDL-specific event actions are accessed by either + // Control or Alt keys. So we quickly check for those. + if(mod & KMOD_ALT) + { + if(key == SDLK_EQUALS) + theDisplay->resize(1); + else if(key == SDLK_MINUS) + theDisplay->resize(-1); + else if(key == SDLK_RETURN) + { +#ifdef WIN32 + // Disable fullscreen OpenGL mode - FIXME + if(theSettings->getString("video") == "gl") + theDisplay->showMessage("Fullscreen OpenGL disabled"); + else + theDisplay->toggleFullscreen(); +#else + theDisplay->toggleFullscreen(); +#endif + } +#ifdef DISPLAY_OPENGL + else if(key == SDLK_f && theUseOpenGLFlag) + ((FrameBufferGL*)theDisplay)->toggleFilter(); +#endif +#ifdef DEVELOPER_SUPPORT + else if(key == SDLK_END) // Alt-End increases XStart + { + theConsole->changeXStart(1); + theDisplay->resize(0); + } + else if(key == SDLK_HOME) // Alt-Home decreases XStart + { + theConsole->changeXStart(0); + theDisplay->resize(0); + } + else if(key == SDLK_PAGEUP) // Alt-PageUp increases YStart + { + theConsole->changeYStart(1); + theDisplay->resize(0); + } + else if(key == SDLK_PAGEDOWN) // Alt-PageDown decreases YStart + { + theConsole->changeYStart(0); + theDisplay->resize(0); + } +#endif + } + else if(mod & KMOD_CTRL) + { + if(key == SDLK_g) + { + // don't change grabmouse in fullscreen mode + if(!theDisplay->fullScreen()) + { + theGrabMouseIndicator = !theGrabMouseIndicator; + theSettings->setBool("grabmouse", theGrabMouseIndicator); + theDisplay->grabMouse(theGrabMouseIndicator); + } + } + else if(key == SDLK_h) + { + // don't change hidecursor in fullscreen mode + if(!theDisplay->fullScreen()) + { + theHideCursorIndicator = !theHideCursorIndicator; + theSettings->setBool("hidecursor", theHideCursorIndicator); + theDisplay->showCursor(!theHideCursorIndicator); + } + } +#ifdef DEVELOPER_SUPPORT + else if(key == SDLK_f) // Ctrl-f toggles NTSC/PAL mode + { + theConsole->toggleFormat(); + theDisplay->setupPalette(); + } + else if(key == SDLK_p) // Ctrl-p toggles different palettes + { + theConsole->togglePalette(); + theDisplay->setupPalette(); + } + else if(key == SDLK_END) // Ctrl-End increases Width + { + theConsole->changeWidth(1); + theDisplay->resize(0); + } + else if(key == SDLK_HOME) // Ctrl-Home decreases Width + { + theConsole->changeWidth(0); + theDisplay->resize(0); + } + else if(key == SDLK_PAGEUP) // Ctrl-PageUp increases Height + { + theConsole->changeHeight(1); + theDisplay->resize(0); + } + else if(key == SDLK_PAGEDOWN) // Ctrl-PageDown decreases Height + { + theConsole->changeHeight(0); + theDisplay->resize(0); + } + else if(key == SDLK_s) // Ctrl-s saves properties to a file + { + if(theConsole->settings().getBool("mergeprops")) // Attempt to merge with propertiesSet + { + theConsole->saveProperties(theSettings->userPropertiesFilename(), true); + } + else // Save to file in home directory + { + string newPropertiesFile = theConsole->settings().baseDir() + "/" + \ + theConsole->properties().get("Cartridge.Name") + ".pro"; + replace(newPropertiesFile.begin(), newPropertiesFile.end(), ' ', '_'); + theConsole->saveProperties(newPropertiesFile); + } + } +#endif + } + else // check all the other keys + { + for(unsigned int i = 0; i < sizeof(keyList) / sizeof(Switches); ++i) + { + if(keyList[i].scanCode == key) + theConsole->eventHandler().sendKeyEvent(keyList[i].keyCode, 1); + } + } + } + else if(event.type == SDL_KEYUP) + { + key = event.key.keysym.sym; + type = event.type; + + for(unsigned int i = 0; i < sizeof(keyList) / sizeof(Switches); ++i) + { + if(keyList[i].scanCode == key) + theConsole->eventHandler().sendKeyEvent(keyList[i].keyCode, 0); + } + } + else if(event.type == SDL_MOUSEMOTION) + { + Int32 resistance; + uInt32 zoom = theDisplay->zoomLevel(); + Int32 width = theDisplay->width() * zoom; + Event::Type type = Event::NoType; + + // Grabmouse and hidecursor introduce some lag into the mouse movement, + // so we need to fudge the numbers a bit + if(theGrabMouseIndicator && theHideCursorIndicator) + { + mouseX = (int)((float)mouseX + (float)event.motion.xrel + * 1.5 * (float) zoom); + } + else + { + mouseX = mouseX + event.motion.xrel * zoom; + } + + // Check to make sure mouseX is within the game window + if(mouseX < 0) + mouseX = 0; + else if(mouseX > width) + mouseX = width; + + resistance = (Int32)(1000000.0 * (width - mouseX) / width); + + // Now, set the event of the correct paddle to the calculated resistance + if(thePaddleMode == 0) + type = Event::PaddleZeroResistance; + else if(thePaddleMode == 1) + type = Event::PaddleOneResistance; + else if(thePaddleMode == 2) + type = Event::PaddleTwoResistance; + else if(thePaddleMode == 3) + type = Event::PaddleThreeResistance; + + theConsole->eventHandler().sendEvent(type, resistance); + } + else if(event.type == SDL_MOUSEBUTTONDOWN || event.type == SDL_MOUSEBUTTONUP) + { + Event::Type type = Event::LastType; + Int32 value; + + if(event.type == SDL_MOUSEBUTTONDOWN) + value = 1; + else + value = 0; + + if(thePaddleMode == 0) + type = Event::PaddleZeroFire; + else if(thePaddleMode == 1) + type = Event::PaddleOneFire; + else if(thePaddleMode == 2) + type = Event::PaddleTwoFire; + else if(thePaddleMode == 3) + type = Event::PaddleThreeFire; + + theConsole->eventHandler().sendEvent(type, value); + } + else if(event.type == SDL_ACTIVEEVENT) + { + if((event.active.state & SDL_APPACTIVE) && (event.active.gain == 0)) + { + if(!theConsole->eventHandler().doPause()) + { + theConsole->eventHandler().sendEvent(Event::Pause, 1); + } + } + } + else if(event.type == SDL_QUIT) + { + theConsole->eventHandler().sendEvent(Event::Quit, 1); + } + else if(event.type == SDL_VIDEOEXPOSE) + { + theDisplay->refresh(); + } + +#ifdef JOYSTICK_SUPPORT + // Read joystick events and modify event states + StellaEvent::JoyStick stick; + StellaEvent::JoyCode code; + Int32 state; + Uint8 axis; + Uint8 button; + Int32 resistance; + Sint16 value; + JoyType type; + + if(event.jbutton.which >= StellaEvent::LastJSTICK) + return; + + stick = joyList[event.jbutton.which]; + type = theJoysticks[event.jbutton.which].type; + + // Figure put what type of joystick we're dealing with + // Stelladaptors behave differently, and can't be remapped + switch(type) + { + case JT_NONE: + break; + + case JT_REGULAR: + + if((event.type == SDL_JOYBUTTONDOWN) || (event.type == SDL_JOYBUTTONUP)) + { + if(event.jbutton.button >= StellaEvent::LastJCODE) + return; + + code = joyButtonList[event.jbutton.button]; + state = event.jbutton.state == SDL_PRESSED ? 1 : 0; + + theConsole->eventHandler().sendJoyEvent(stick, code, state); + } + else if(event.type == SDL_JOYAXISMOTION) + { + axis = event.jaxis.axis; + value = event.jaxis.value; + + if(axis == 0) // x-axis + { + theConsole->eventHandler().sendJoyEvent(stick, StellaEvent::JAXIS_LEFT, + (value < -16384) ? 1 : 0); + theConsole->eventHandler().sendJoyEvent(stick, StellaEvent::JAXIS_RIGHT, + (value > 16384) ? 1 : 0); + } + else if(axis == 1) // y-axis + { + theConsole->eventHandler().sendJoyEvent(stick, StellaEvent::JAXIS_UP, + (value < -16384) ? 1 : 0); + theConsole->eventHandler().sendJoyEvent(stick, StellaEvent::JAXIS_DOWN, + (value > 16384) ? 1 : 0); + } + } + break; // Regular joystick + + case JT_STELLADAPTOR_1: + case JT_STELLADAPTOR_2: + + if((event.type == SDL_JOYBUTTONDOWN) || (event.type == SDL_JOYBUTTONUP)) + { + button = event.jbutton.button; + state = event.jbutton.state == SDL_PRESSED ? 1 : 0; + + // Send button events for the joysticks/paddles + if(button == 0) + { + if(type == JT_STELLADAPTOR_1) + { + theConsole->eventHandler().sendEvent(Event::JoystickZeroFire, state); + theConsole->eventHandler().sendEvent(Event::DrivingZeroFire, state); + theConsole->eventHandler().sendEvent(Event::PaddleZeroFire, state); + } + else + { + theConsole->eventHandler().sendEvent(Event::JoystickOneFire, state); + theConsole->eventHandler().sendEvent(Event::DrivingOneFire, state); + theConsole->eventHandler().sendEvent(Event::PaddleTwoFire, state); + } + } + else if(button == 1) + { + if(type == JT_STELLADAPTOR_1) + theConsole->eventHandler().sendEvent(Event::PaddleOneFire, state); + else + theConsole->eventHandler().sendEvent(Event::PaddleThreeFire, state); + } + } + else if(event.type == SDL_JOYAXISMOTION) + { + axis = event.jaxis.axis; + value = event.jaxis.value; + + // Send axis events for the joysticks + theConsole->eventHandler().sendEvent(SA_Axis[type-2][axis][0], + (value < -16384) ? 1 : 0); + theConsole->eventHandler().sendEvent(SA_Axis[type-2][axis][1], + (value > 16384) ? 1 : 0); + + // Send axis events for the paddles + resistance = (Int32) (1000000.0 * (32767 - value) / 65534); + theConsole->eventHandler().sendEvent(SA_Axis[type-2][axis][2], resistance); + + // Send events for the driving controllers + if(axis == 1) + { + if(value <= -16384-4096) + theConsole->eventHandler().sendEvent(SA_DrivingValue[type-2],2); + else if(value > 16384+4096) + theConsole->eventHandler().sendEvent(SA_DrivingValue[type-2],1); + else if(value >= 16384-4096) + theConsole->eventHandler().sendEvent(SA_DrivingValue[type-2],0); + else + theConsole->eventHandler().sendEvent(SA_DrivingValue[type-2],3); + } + } + break; // Stelladaptor joystick + + default: + break; + } +#endif + } +} + + +/** + Setup the properties set by first checking for a user file + "$HOME/.stella/stella.pro", then a system-wide file "/etc/stella.pro". + Return false if neither file is found, else return true. + + @param set The properties set to setup +*/ +bool setupProperties(PropertiesSet& set) +{ + bool useMemList = false; + string theAlternateProFile = theSettings->getString("altpro"); + string theUserProFile = theSettings->userPropertiesFilename(); + string theSystemProFile = theSettings->systemPropertiesFilename(); + +#ifdef DEVELOPER_SUPPORT + // If the user wishes to merge any property modifications to the + // PropertiesSet file, then the PropertiesSet file MUST be loaded + // into memory. + useMemList = theSettings->getBool("mergeprops"); +#endif + + // Check to see if the user has specified an alternate .pro file. + + if(theAlternateProFile != "") + { + set.load(theAlternateProFile, &Console::defaultProperties(), useMemList); + return true; + } + + if(theUserProFile != "") + { + set.load(theUserProFile, &Console::defaultProperties(), useMemList); + return true; + } + else if(theSystemProFile != "") + { + set.load(theSystemProFile, &Console::defaultProperties(), useMemList); + return true; + } + else + { + set.load("", &Console::defaultProperties(), false); + return true; + } +} + + +/** + Does general cleanup in case any operation failed (or at end of program). +*/ +void cleanup() +{ +#ifdef JOYSTICK_SUPPORT + if(SDL_WasInit(SDL_INIT_JOYSTICK) & SDL_INIT_JOYSTICK) + { + for(uInt32 i = 0; i < StellaEvent::LastJSTICK; i++) + { + if(SDL_JoystickOpened(i)) + SDL_JoystickClose(theJoysticks[i].stick); + } + } +#endif + + if(theSettings) + delete theSettings; + + if(theConsole) + delete theConsole; + + if(theSound) + delete theSound; + + if(theDisplay) + delete theDisplay; + + if(SDL_WasInit(SDL_INIT_VIDEO) & SDL_INIT_VIDEO) + SDL_Quit(); +} + + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +int main(int argc, char* argv[]) +{ +#if defined(UNIX) + theSettings = new SettingsUNIX(); +#elif defined(WIN32) + theSettings = new SettingsWin32(); +#else + #error Unsupported platform! +#endif + if(!theSettings) + { + cleanup(); + return 0; + } + theSettings->loadConfig(); + + // Take care of commandline arguments + if(!theSettings->loadCommandLine(argc, argv)) + { + string message = "Stella version 1.4_cvs\n\nUsage: stella [options ...] romfile"; + theSettings->usage(message); + cleanup(); + return 0; + } + + // Cache some settings so they don't have to be repeatedly searched for + thePaddleMode = theSettings->getInt("paddle"); + theShowInfoFlag = theSettings->getBool("showinfo"); + theGrabMouseIndicator = theSettings->getBool("grabmouse"); + theHideCursorIndicator = theSettings->getBool("hidecursor"); + + // Request that the SDL window be centered, if possible + putenv("SDL_VIDEO_CENTERED=1"); + + // Get a pointer to the file which contains the cartridge ROM + const char* file = argv[argc - 1]; + + // Open the cartridge image and read it in + ifstream in(file, ios_base::binary); + if(!in) + { + cerr << "ERROR: Couldn't open " << file << "..." << endl; + string message = "Stella version 1.4_cvs\n\nUsage: stella [options ...] romfile"; + theSettings->usage(message); + cleanup(); + return 0; + } + + uInt8* image = new uInt8[512 * 1024]; + in.read((char*)image, 512 * 1024); + uInt32 size = in.gcount(); + in.close(); + + // Create a properties set for us to use and set it up + PropertiesSet propertiesSet; + if(!setupProperties(propertiesSet)) + { + delete[] image; + cleanup(); + return 0; + } + + // Create an SDL window + string videodriver = theSettings->getString("video"); + if(videodriver == "soft") + { + theDisplay = new FrameBufferSoft(); + if(theShowInfoFlag) + cout << "Using software mode for video.\n"; + } +#ifdef DISPLAY_OPENGL + else if(videodriver == "gl") + { + #ifdef WIN32 + // Disable fullscreen OpenGL mode in Windows for now - FIXME + theSettings->setBool("fullscreen", false); + #endif + + theDisplay = new FrameBufferGL(); + theUseOpenGLFlag = true; + if(theShowInfoFlag) + cout << "Using OpenGL mode for video.\n"; + } +#endif + else // a driver that doesn't exist was requested, so use software mode + { + theDisplay = new FrameBufferSoft(); + if(theShowInfoFlag) + cout << "Using software mode for video.\n"; + } + + if(!theDisplay) + { + cerr << "ERROR: Couldn't set up display.\n"; + delete[] image; + cleanup(); + return 0; + } + + // Create a sound object for playing audio + if(theSettings->getBool("sound")) + { + uInt32 fragsize = theSettings->getInt("fragsize"); + theSound = new SoundSDL(fragsize); + if(theShowInfoFlag) + { + cout << "Sound enabled, using fragment size = " << fragsize; + cout << "." << endl; + } + } + else // even if sound has been disabled, we still need a sound object + { + theSound = new Sound(); + if(theShowInfoFlag) + cout << "Sound disabled.\n"; + } + theSound->setVolume(theSettings->getInt("volume")); + + // Get just the filename of the file containing the ROM image + const char* filename = (!strrchr(file, '/')) ? file : strrchr(file, '/') + 1; + + // Create the 2600 game console + theConsole = new Console(image, size, filename, *theSettings, propertiesSet, + *theDisplay, *theSound); + + // Free the image since we don't need it any longer + delete[] image; + + // Setup the SDL joysticks + // This must be done after the console is created + if(!setupJoystick()) + { + cerr << "ERROR: Couldn't set up joysticks.\n"; + cleanup(); + return 0; + } + + // These variables are common to both timing options + // and are needed to calculate the overall frames per second. + uInt32 frameTime = 0, numberOfFrames = 0; + + if(theSettings->getBool("accurate")) // normal, CPU-intensive timing + { + // Set up accurate timing stuff + uInt32 startTime, delta; + uInt32 timePerFrame = (uInt32)(1000000.0 / (double)theSettings->getInt("framerate")); + + // Set the base for the timers + frameTime = 0; + + // Main game loop + for(;;) + { + // Exit if the user wants to quit + if(theConsole->eventHandler().doQuit()) + { + break; + } + + startTime = getTicks(); + handleEvents(); + theConsole->update(); + + // Now, waste time if we need to so that we are at the desired frame rate + for(;;) + { + delta = getTicks() - startTime; + + if(delta >= timePerFrame) + break; + } + + frameTime += getTicks() - startTime; + ++numberOfFrames; + } + } + else // less accurate, less CPU-intensive timing + { + // Set up less accurate timing stuff + uInt32 startTime, virtualTime, currentTime; + uInt32 timePerFrame = (uInt32)(1000000.0 / (double)theSettings->getInt("framerate")); + + // Set the base for the timers + virtualTime = getTicks(); + frameTime = 0; + + // Main game loop + for(;;) + { + // Exit if the user wants to quit + if(theConsole->eventHandler().doQuit()) + { + break; + } + + startTime = getTicks(); + handleEvents(); + theConsole->update(); + + currentTime = getTicks(); + virtualTime += timePerFrame; + if(currentTime < virtualTime) + { + SDL_Delay((virtualTime - currentTime)/1000); + } + + currentTime = getTicks() - startTime; + frameTime += currentTime; + ++numberOfFrames; + } + } + + if(theShowInfoFlag) + { + double executionTime = (double) frameTime / 1000000.0; + double framesPerSecond = (double) numberOfFrames / executionTime; + + cout << endl; + cout << numberOfFrames << " total frames drawn\n"; + cout << framesPerSecond << " frames/second\n"; + cout << endl; + cout << "Cartridge Name: " << theConsole->properties().get("Cartridge.Name"); + cout << endl; + cout << "Cartridge MD5: " << theConsole->properties().get("Cartridge.MD5"); + cout << endl << endl; + } + + // Cleanup time ... + cleanup(); + return 0; +}