Back to index

supertuxkart  0.5+dfsg1
sdldrv.cpp
Go to the documentation of this file.
00001 //  $Id: plibdrv.cpp 757 2006-09-11 22:27:39Z hiker $
00002 //
00003 //  SuperTuxKart - a fun racing game with go-kart
00004 //  Copyright (C) 2004 Steve Baker <sjbaker1@airmail.net>
00005 //
00006 //  This program is free software; you can redistribute it and/or
00007 //  modify it under the terms of the GNU General Public License
00008 //  as published by the Free Software Foundation; either version 2
00009 //  of the License, or (at your option) any later version.
00010 //
00011 //  This program is distributed in the hope that it will be useful,
00012 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
00013 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014 //  GNU General Public License for more details.
00015 //
00016 //  You should have received a copy of the GNU General Public License
00017 //  along with this program; if not, write to the Free Software
00018 //  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
00019 
00020 #include <map>
00021 
00022 #include <SDL/SDL.h>
00023 #include <string>
00024 #include <iostream>
00025 #include <sstream>
00026 #include <algorithm>
00027 
00028 #include "input.hpp"
00029 #include "actionmap.hpp"
00030 #include "user_config.hpp"
00031 #include "sdldrv.hpp"
00032 #include "material_manager.hpp"
00033 #include "kart_properties_manager.hpp"
00034 #include "game_manager.hpp"
00035 #include "herring_manager.hpp"
00036 #include "collectable_manager.hpp"
00037 #include "attachment_manager.hpp"
00038 #include "projectile_manager.hpp"
00039 #include "loader.hpp"
00040 #include "gui/menu_manager.hpp"
00041 #include "widget_manager.hpp"
00042 #include "player.hpp"
00043 #include "gui/font.hpp"
00044 #include "user_config.hpp"
00045 
00046 #define DEADZONE_MOUSE 150
00047 #define DEADZONE_MOUSE_SENSE 200
00048 #define DEADZONE_JOYSTICK 2000
00049 
00050 #define MULTIPLIER_MOUSE 750
00051 
00052 using namespace std;
00053 
00054 SDLDriver *inputDriver;
00055 
00056 //-----------------------------------------------------------------------------
00057 SDLDriver::SDLDriver()
00058        : sensedInput(0), actionMap(0), mainSurface(0), flags(0), stickInfos(0),
00059        mode(BOOTSTRAP), mouseValX(0), mouseValY(0)
00060 {
00061     SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK | SDL_INIT_TIMER);
00062 
00063     SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
00064     SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
00065     SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
00066     SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);
00067     SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
00068     SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
00069     SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 1);
00070 
00071     flags = SDL_OPENGL | SDL_HWSURFACE;
00072         
00073     //detect if previous resolution crashed STK
00074     if (user_config->m_crashed)
00075     {
00076        //STK crashed last time
00077        user_config->m_crashed = false;  //reset flag
00078        // set window mode as a precaution
00079        user_config->m_fullscreen = false;
00080        // blacklist the res if not already done
00081        std::ostringstream o;
00082        o << user_config->m_width << "x" << user_config->m_height;
00083        std::string res = o.str();
00084        if (std::find(user_config->m_blacklist_res.begin(),
00085          user_config->m_blacklist_res.end(),res) == user_config->m_blacklist_res.end())
00086        {
00087               user_config->m_blacklist_res.push_back (o.str());
00088        }
00089        //use prev screen res settings if available
00090        if (user_config->m_width != user_config->m_prev_width
00091               || user_config->m_height != user_config->m_prev_height)
00092        {
00093               user_config->m_width = user_config->m_prev_width;
00094               user_config->m_height = user_config->m_prev_height;
00095        }
00096        else //set 'safe' resolution to return to
00097        {
00098               user_config->m_width = user_config->m_prev_width = 800;
00099               user_config->m_height = user_config->m_prev_height = 600;
00100        }
00101     }
00102     
00103        if(user_config->m_fullscreen)
00104         flags |= SDL_FULLSCREEN;
00105         
00106     setVideoMode(false);
00107 
00108     SDL_JoystickEventState(SDL_ENABLE);
00109 
00110        initStickInfos();
00111        
00112     SDL_WM_SetCaption("SuperTuxKart", NULL);
00113 
00114        // Get into menu mode initially.
00115        setMode(MENU);
00116 }
00117 
00118 void
00119 SDLDriver::initStickInfos()
00120 {
00121        int nextIndex = 0;
00122        
00123        // Prepare a list of connected joysticks.
00124     const int numSticks = SDL_NumJoysticks();
00125        stickInfos = new StickInfo *[numSticks];
00126     vector<StickInfo *> *si = new vector<StickInfo *>;
00127     for (int i = 0; i < numSticks; i++)
00128         si->push_back(stickInfos[i] = new StickInfo(i));
00129               
00130        // Get the list of known configs and make a copy of it.
00131        vector<UserConfig::StickConfig *> *sc
00132               = new vector<UserConfig::StickConfig *>(*user_config->getStickConfigs());
00133        
00134        bool match;
00135        vector<StickInfo *>::iterator si_ite = si->begin();
00136 
00137     // FIXME: Visual Studio triggers an exception (in debug mode) when si 
00138     // becomes empty (incompatible iterators). This is apparently caused
00139     // by using erase. For now I added a work around by checking for 
00140     // si->size()>0, which solves the problem for me. But I have only one
00141     // gamepad, I'd suspect that with more gamepads the problem still exists.
00142        while (si->size()>0 && si_ite != si->end())
00143        {
00144               match = false;
00145               
00146               vector<UserConfig::StickConfig *>::iterator sc_ite = sc->begin();
00147               while (sc_ite != sc->end())
00148               {
00149                      if (nextIndex <= (*sc_ite)->preferredIndex)
00150                             nextIndex = (*sc_ite)->preferredIndex + 1;
00151                      
00152                      if (!(*si_ite)->id->compare((*sc_ite)->id))
00153                      {
00154                             // Connected stick matches a stored one.
00155                             
00156                             // Copy important properties.
00157                             
00158                             // Deadzone is taken only if its not null.
00159                             if ((*sc_ite)->deadzone)
00160                                    (*si_ite)->deadzone = (*sc_ite)->deadzone;
00161                                              
00162                             // Restore former used index and other properties.
00163                             (*si_ite)->index = (*sc_ite)->preferredIndex;
00164                             
00165                             // Remove matching entries from the list to prevent double
00166                             // allocation.
00167                             sc->erase(sc_ite);
00168                             si->erase(si_ite);
00169                             
00170                             match = true;
00171                             
00172                             break;
00173                      }
00174                             
00175                      sc_ite++;
00176               }
00177               
00178               if (!match)
00179                      si_ite++;
00180        }
00181        delete sc;
00182        
00183        // si now contains all those stick infos which have no stick config yet
00184        // and nextIndex is set to the next free index. 
00185        
00186        // Now add all those new sticks and generate a config for them.
00187        si_ite = si->begin();
00188        while (si_ite != si->end())
00189        {
00190               (*si_ite)->index = nextIndex;
00191               
00192               UserConfig::StickConfig *sc = new UserConfig::StickConfig(*(*si_ite)->id);
00193               sc->preferredIndex = nextIndex;
00194               sc->deadzone = DEADZONE_JOYSTICK;
00195 
00196               user_config->addStickConfig(sc);
00197               
00198               nextIndex++;
00199               si_ite++;
00200        }
00201        
00202        delete si;
00203 }
00204 
00205 //-----------------------------------------------------------------------------
00206 void
00207 SDLDriver::showPointer()
00208 {
00209   SDL_ShowCursor(SDL_ENABLE);
00210 }
00211 //-----------------------------------------------------------------------------
00212 void
00213 SDLDriver::hidePointer()
00214 {
00215   SDL_ShowCursor(SDL_DISABLE);
00216 }
00217 //-----------------------------------------------------------------------------
00218 void
00219 SDLDriver::toggleFullscreen(bool resetTextures)
00220 {
00221     user_config->m_fullscreen = !user_config->m_fullscreen;
00222 
00223     flags = SDL_OPENGL | SDL_HWSURFACE;
00224 
00225     if(user_config->m_fullscreen)
00226     {
00227         flags |= SDL_FULLSCREEN;
00228 
00229         if(menu_manager->isSomewhereOnStack(MENUID_RACE))
00230           showPointer();
00231           
00232        // Store settings in user config file in case new video mode
00233        // causes a crash
00234        user_config->m_crashed = true; //set flag. 
00235        user_config->saveConfig();
00236     }
00237     else if(menu_manager->isSomewhereOnStack(MENUID_RACE))
00238         hidePointer();
00239             
00240     setVideoMode(resetTextures);
00241     
00242 }
00243 
00244 //-----------------------------------------------------------------------------
00245 void
00246 SDLDriver::setVideoMode(bool resetTextures)
00247 {
00248     //Is SDL_FreeSurface necessary? SDL wiki says not??
00249     SDL_FreeSurface(mainSurface);
00250     mainSurface = SDL_SetVideoMode(user_config->m_width, user_config->m_height, 0, flags);
00251 
00252 #if defined(WIN32) || defined(__APPLE__)
00253     if(resetTextures)
00254     {
00255         // Clear plib internal texture cache
00256         loader->endLoad();
00257 
00258         // Windows needs to reload all textures, display lists, ... which means
00259         // that all models have to be reloaded. So first, free all textures,
00260         // models, then reload the textures from materials.dat, then reload
00261         // all models, textures etc.
00262 
00263        //        startScreen             -> removeTextures();
00264         attachment_manager      -> removeTextures();
00265         projectile_manager      -> removeTextures();
00266         herring_manager         -> removeTextures();
00267         kart_properties_manager -> removeTextures();
00268         collectable_manager     -> removeTextures();
00269 
00270         material_manager->reInit();
00271 
00272 
00273         collectable_manager     -> loadCollectables();
00274         kart_properties_manager -> loadKartData();
00275         herring_manager         -> loadDefaultHerrings();
00276         projectile_manager      -> loadData();
00277         attachment_manager      -> loadModels();
00278 
00279        //        startScreen             -> installMaterial();
00280 
00281         //FIXME: the font reinit funcs should be inside the font class
00282         //Reinit fonts
00283         delete_fonts();
00284         init_fonts();
00285 
00286         //TODO: this function probably will get deleted in the future; if
00287         //so, the widget_manager.hpp include has no other reason to be here.
00288         widget_manager->reloadFonts();
00289     }
00290 #endif
00291 }
00292 
00293 //-----------------------------------------------------------------------------
00294 SDLDriver::~SDLDriver()
00295 {
00296     const int NUM_STICKS = SDL_NumJoysticks();
00297     for (int i = 0; i < NUM_STICKS; i++)
00298               delete stickInfos[i];
00299        
00300     delete [] stickInfos;
00301 
00302     SDL_FreeSurface(mainSurface);
00303 
00304     SDL_Quit();
00305 }
00306 
00307 //-----------------------------------------------------------------------------
00320 void
00321 SDLDriver::input(InputType type, int id0, int id1, int id2, int value)
00322 {
00323     BaseGUI* menu = menu_manager->getCurrentMenu();
00324 
00325        GameAction ga = actionMap->getEntry(type, id0, id1, id2);
00326 
00327        if (menu != NULL)
00328        {
00329               // Act different in input sensing mode.
00330               if (mode == INPUT_SENSE)
00331               {
00332                      // Input sensing should be canceled.
00333                      if (ga == GA_LEAVE)
00334                      {
00335                             menu->handle(GA_SENSE_CANCEL, value);
00336                      }
00337                      // Stores the sensed input when the button/key/axes/<whatever> is
00338                      // released only and is not used in a fixed mapping.
00339                      else if (!(value || user_config->isFixedInput(type, id0, id1, id2)))
00340                      {
00341                             sensedInput->type = type;
00342                             sensedInput->id0 = id0;
00343                             sensedInput->id1 = id1;
00344                             sensedInput->id2 = id2;
00345 
00346                             // Notify the completion of the input sensing.
00347                             menu->handle(GA_SENSE_COMPLETE, 0);
00348                      }
00349               }
00350               else if (ga != GA_NULL)
00351               {
00352                      // Lets the currently active menu handle the GameAction.
00353                      menu->handle(ga, value);
00354               }
00355        }
00356 }
00357 
00358 //-----------------------------------------------------------------------------
00376 void
00377 SDLDriver::input()
00378 {
00379     SDL_Event ev;
00380        /* Logical joystick index that is reported to the higher game APIs which
00381         * may not be equal to SDL's joystick index.
00382         */
00383        int stickIndex;
00384 
00385     while(SDL_PollEvent(&ev))
00386     {
00387         switch(ev.type)
00388         {
00389         case SDL_QUIT:
00390             game_manager->abort();
00391             break;
00392 
00393         case SDL_KEYUP:
00394               input(IT_KEYBOARD, ev.key.keysym.sym, 0, 0, 0);
00395             break;
00396         case SDL_KEYDOWN:
00397                      if (mode == LOWLEVEL)
00398                      {
00399                             // Unicode translation in SDL is only done for keydown events.
00400                             // Therefore for lowlevel keyboard handling we provide no notion
00401                             // of whether a key was pressed or released.
00402                             menu_manager->getCurrentMenu()
00403                                    ->inputKeyboard(ev.key.keysym.sym,
00404                                                                ev.key.keysym.unicode);
00405                      }
00406             input(IT_KEYBOARD, ev.key.keysym.sym,
00407                               ev.key.keysym.unicode, 0, 32768);
00408 
00409             break;
00410 
00411         case SDL_MOUSEMOTION:
00412                      // Reports absolute pointer values on a separate path to the menu
00413                      // system to avoid the trouble that arises because all other input
00414                      // methods have only one value to inspect (pressed/release,
00415                      // axis value) while the pointer has two.
00416                      if (!mode)
00417                      {
00418                             BaseGUI* menu = menu_manager->getCurrentMenu();
00419                             if (menu != NULL)
00420                                    menu->inputPointer(ev.motion.x, mainSurface->h - ev.motion.y);
00421                      }
00422                      // If sensing input mouse movements are made less sensitive in order
00423                      // to avoid it being detected unwantedly.
00424                      else if (mode == INPUT_SENSE)
00425                      {
00426                             if (ev.motion.xrel <= -DEADZONE_MOUSE_SENSE)
00427                                    input(IT_MOUSEMOTION, 0, AD_NEGATIVE, 0, 0);
00428                             else if (ev.motion.xrel >= DEADZONE_MOUSE_SENSE)
00429                                    input(IT_MOUSEMOTION, 0, AD_POSITIVE, 0, 0);
00430 
00431                             if (ev.motion.yrel <= -DEADZONE_MOUSE_SENSE)
00432                                    input(IT_MOUSEMOTION, 1, AD_NEGATIVE, 0, 0);
00433                             else if (ev.motion.yrel >= DEADZONE_MOUSE_SENSE)
00434                                    input(IT_MOUSEMOTION, 1, AD_POSITIVE, 0, 0);
00435                      }
00436                      else
00437                      {
00438                             // Calculates new values for the mouse helper variables. It
00439                             // keeps them in the [-32768, 32768] range. The same values are
00440                             // used by SDL for stick axes. 
00441                             mouseValX = std::max(-32768, std::min(32768,
00442                                                                                              mouseValX + ev.motion.xrel
00443                                                                                              * MULTIPLIER_MOUSE));
00444                             mouseValY = std::max(-32768,
00445                                                                 std::min(32768, mouseValY + ev.motion.yrel
00446                                                                                             * MULTIPLIER_MOUSE));
00447                      }
00448                      break;
00449         case SDL_MOUSEBUTTONUP:
00450             input(IT_MOUSEBUTTON, ev.button.button, 0, 0, 0);
00451             break;
00452 
00453         case SDL_MOUSEBUTTONDOWN:
00454             input(IT_MOUSEBUTTON, ev.button.button, 0, 0, 32768);
00455             break;
00456 
00457         case SDL_JOYAXISMOTION:
00458                      stickIndex = stickInfos[ev.jaxis.which]->index;
00459                      // If the joystick axis exceeds the deadzone report the input.
00460                      // In menu mode (mode = MENU = 0) the joystick number is reported
00461                      // to be zero in all cases. This has the neat effect that all
00462                      // joysticks can be used to control the menu.
00463             if(ev.jaxis.value <= -stickInfos[ev.jaxis.which]->deadzone)
00464                      {
00465                 input(IT_STICKMOTION, !mode ? 0 : stickIndex,
00466                                      ev.jaxis.axis, AD_NEGATIVE, -ev.jaxis.value);
00467                             stickInfos[ev.jaxis.which]->prevAxisDirections[ev.jaxis.axis]
00468                                    = AD_NEGATIVE;
00469                      }
00470                      else if(ev.jaxis.value >= stickInfos[ev.jaxis.which]->deadzone)
00471                      {
00472                             input(IT_STICKMOTION, !mode ? 0 : stickIndex,
00473                                      ev.jaxis.axis, AD_POSITIVE, ev.jaxis.value);
00474                             stickInfos[ev.jaxis.which]->prevAxisDirections[ev.jaxis.axis]
00475                                    = AD_POSITIVE;
00476                      }
00477                      else
00478                      {
00479                             // Axis stands still: This is reported once for digital axes and
00480                             // can be called multipled times for analog ones. Uses the
00481                             // previous direction in which the axis was triggered to
00482                             // determine which one has to be brought into the released
00483                             // state. This allows us to regard two directions of an axis
00484                             // as completely independent input variants (as if they where
00485                             // two buttons).                          
00486                             if (stickInfos[ev.jaxis.which]
00487                                    ->prevAxisDirections[ev.jaxis.axis] == AD_NEGATIVE)
00488                             input(IT_STICKMOTION, !mode ? 0 : stickIndex,
00489                                             ev.jaxis.axis, AD_NEGATIVE, 0);
00490                             else if (stickInfos[ev.jaxis.which]
00491                                            ->prevAxisDirections[ev.jaxis.axis] == AD_POSITIVE)
00492                             input(IT_STICKMOTION, !mode ? 0 : stickIndex,
00493                                             ev.jaxis.axis, AD_POSITIVE, 0);
00494                             
00495                             stickInfos[ev.jaxis.which]->prevAxisDirections[ev.jaxis.axis]
00496                                    = AD_NEUTRAL;
00497                      }
00498 
00499             break;
00500         case SDL_JOYBUTTONUP:
00501                      stickIndex = stickInfos[ev.jbutton.which]->index;
00502                                    
00503                      // See the SDL_JOYAXISMOTION case label because of !mode thingie.
00504             input(IT_STICKBUTTON, !mode ? 0 : stickIndex, ev.jbutton.button, 0,
00505                   0);
00506             break;
00507         case SDL_JOYBUTTONDOWN:
00508                      stickIndex = stickInfos[ev.jbutton.which]->index;
00509 
00510                      // See the SDL_JOYAXISMOTION case label because of !mode thingie.
00511             input(IT_STICKBUTTON, !mode ? 0 : stickIndex, ev.jbutton.button, 0,
00512                   32768);
00513             break;
00514         case SDL_USEREVENT:
00515         // used in display_res_confirm for the countdown timer
00516         (menu_manager->getCurrentMenu())->countdown();
00517         
00518         }  // switch
00519     }   // while (SDL_PollEvent())
00520 
00521     // Makes mouse behave like an analog axis.
00522        if (mouseValX <= -DEADZONE_MOUSE)
00523          input(IT_MOUSEMOTION, 0, AD_NEGATIVE, 0, -mouseValX);
00524        else if (mouseValX >= DEADZONE_MOUSE)
00525          input(IT_MOUSEMOTION, 0, AD_POSITIVE, 0, mouseValX);
00526        else
00527          mouseValX = 0;
00528 
00529        if (mouseValY <= -DEADZONE_MOUSE)
00530          input(IT_MOUSEMOTION, 1, AD_NEGATIVE, 0, -mouseValY);
00531        else if (mouseValY >= DEADZONE_MOUSE)
00532          input(IT_MOUSEMOTION, 1, AD_POSITIVE, 0, mouseValY);
00533        else
00534          mouseValY = 0;
00535 
00536 }
00537 
00538 //-----------------------------------------------------------------------------
00548 Input &
00549 SDLDriver::getSensedInput()
00550 {
00551        assert (mode == INPUT_SENSE);
00552 
00553        // sensedInput should be available in input sense mode.
00554        assert (sensedInput);
00555        
00556        return *sensedInput;
00557 }
00558 
00559 //-----------------------------------------------------------------------------
00562 bool
00563 SDLDriver::isInMode(InputDriverMode expMode)
00564 {
00565        return mode == expMode;
00566 }
00567 //-----------------------------------------------------------------------------
00607 void
00608 SDLDriver::setMode(InputDriverMode newMode)
00609 {
00610        switch (newMode)
00611        {
00612        case MENU:
00613               switch (mode)
00614               {
00615               case INGAME:
00616                      // Leaving ingame mode.
00617                             
00618                      if (actionMap)
00619                             delete actionMap;
00620        
00621                      // Reset the helper values for the relative mouse movement
00622                      // supresses to the notification of them as an input.
00623                      mouseValX = mouseValY = 0;
00624                      
00625                      showPointer();
00626                      
00627                      // Fall through expected.
00628               case BOOTSTRAP:
00629                      // Leaving boot strap mode.
00630                             
00631                      // Installs the action map for the menu.
00632                      actionMap = user_config->newMenuActionMap();
00633                      
00634                      mode = MENU;
00635 
00636                      break;
00637               case INPUT_SENSE:
00638                      // Leaving input sense mode.
00639                             
00640                      showPointer();
00641                      
00642                      // The order is deliberate just in case someone starts to make
00643                      // STK multithreaded: sensedInput must not be 0 when
00644                      // mode == INPUT_SENSE.
00645                      mode = MENU;
00646                      
00647                   delete sensedInput;
00648               sensedInput = 0;
00649 
00650                      break;
00651               case LOWLEVEL:
00652                      // Leaving lowlevel mode.
00653                             
00654                   SDL_EnableUNICODE(SDL_DISABLE);
00655 
00656                      showPointer();
00657 
00658                      mode = MENU;
00659                      
00660                      break;
00661               default:
00662                      // Something is broken.
00663                      assert (false);
00664               }
00665               
00666               break;
00667        case INGAME:
00668               // We must be in menu mode now in order to switch.
00669               assert (mode == MENU);
00670        
00671               if (actionMap)
00672                      delete actionMap;
00673 
00674               // Installs the action map for the ingame mode.
00675               actionMap = user_config->newIngameActionMap();
00676 
00677               hidePointer();
00678 
00679               mode = INGAME;
00680               
00681               break;
00682        case INPUT_SENSE:
00683               // We must be in menu mode now in order to switch.
00684               assert (mode == MENU);
00685               
00686               // Reset the helper values for the relative mouse movement supresses to
00687               // the notification of them as an input.
00688               mouseValX = mouseValY = 0;
00689 
00690               sensedInput = new Input();
00691        
00692               hidePointer();
00693        
00694               mode = INPUT_SENSE;
00695               
00696               break;
00697        case LOWLEVEL:
00698               // We must be in menu mode now in order to switch.
00699               assert (mode == MENU);
00700               
00701            SDL_EnableUNICODE(SDL_ENABLE);
00702 
00703               hidePointer();
00704 
00705               mode = LOWLEVEL;
00706 
00707               break;
00708        default:
00709               // Invalid mode.
00710               assert(false);
00711        }
00712 }
00713 
00714 SDLDriver::StickInfo::StickInfo(int sdlIndex)
00715 {
00716        sdlJoystick = SDL_JoystickOpen(sdlIndex);
00717        
00718        id = new string(SDL_JoystickName(sdlIndex));
00719        
00720        const int count = SDL_JoystickNumAxes(sdlJoystick);
00721        prevAxisDirections = new AxisDirection[count];
00722        
00723        for (int i = 0; i < count; i++)
00724               prevAxisDirections[i] = AD_NEUTRAL;
00725        
00726        deadzone = DEADZONE_JOYSTICK;
00727        
00728        index = -1;
00729 }
00730 
00731 SDLDriver::StickInfo::~StickInfo()
00732 {
00733        delete id;
00734        
00735        delete prevAxisDirections;
00736        
00737        SDL_JoystickClose(sdlJoystick);
00738 }