1 //
2 // Cross-platform free Puyo-Puyo clone.
3 // Copyright (C) 2006, 2007 Emma's Software
4 //
5 // This program is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; either version 2 of the License, or
8 // (at your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software
17 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 //
19 #if defined (HAVE_CONFIG_H)
20 #include <config.h>
21 #endif // HAVE_CONFIG_H
22 #include <assert.h>
23 #include <cstdlib>
24 #include <iostream>
25 #include <limits>
26 #include <SDL.h>
27 #include <SDL_mixer.h>
28 #include <sstream>
29 #include <stdexcept>
30 #include <string>
31 #include <algorithm>
32 
33 #if defined (IS_WIN32_HOST)
34 #include <windows.h>
35 #endif // IS_WIN32_HOST
36 #if defined (IS_OSX_HOST)
37 extern "C" void OSXAlert (const char *title, const char *message);
38 #endif // IS_OSX_HOST
39 
40 #include "FrameManager.h"
41 #include "FadeInState.h"
42 #include "FadeOutState.h"
43 #include "File.h"
44 #include "IState.h"
45 #include "Options.h"
46 #include "PauseState.h"
47 #include "System.h"
48 #include "VideoErrorState.h"
49 
50 using namespace Amoebax;
51 
52 /// The buffer system to use for audio.
53 #if defined (IS_GP2X_HOST)
54 static const int k_AudioBuffers = 1024; // Otherwise it gets delayed.
55 #else // !IS_GP2X_HOST
56 static const int k_AudioBuffers = 4096;
57 #endif // IS_GP2X_HOST
58 /// The numnber of audio channels.
59 static const int k_AudioChannels = 2;
60 /// The audio format to use.
61 static const int k_AudioFormat = AUDIO_S16SYS;
62 /// The audio rate to use.
63 static const int k_AudioRate = 44100;
64 /// Maximum frame rate (in FPS.)
65 static const float k_FrameRate = 30.0f;
66 /// Maximum screen's vertical resolution (in pixels.)
67 static const float k_ScreenMaxHeight = 960.0f;
68 /// Maximum screen's horizontal resolution (in pixels.)
69 static const float k_ScreenMaxWidth = 1280.0f;
70 /// The time to show the volume level (in ms.)
71 static const int k_VolumeDisplayTime = 1000;
72 
73 System System::m_SystemInstance;
74 
75 #if defined (IS_WIN32_HOST)
76 ///
77 /// \brief Sets the current directory to be the executable's directory.
78 ///
79 /// This is used when on Windows to be at the same directory as the
80 /// executable, so we can load any data as a relative path.
81 ///
82 static void
changeToExecutableDirectory(void)83 changeToExecutableDirectory (void)
84 {
85     // Get the executable path.
86     TCHAR executablePath[MAX_PATH];
87     if ( GetModuleFileName (NULL, executablePath, MAX_PATH - 1) > 0 )
88     {
89         std::string executableDirectory (executablePath);
90         std::string::size_type backslashPosition = executableDirectory.rfind ("\\");
91         if ( std::string::npos != backslashPosition )
92         {
93             executableDirectory.erase (backslashPosition);
94             File::ChangeWorkDirectory (executableDirectory);
95         }
96     }
97 }
98 #endif // IS_WIN32_HOST
99 
100 ///
101 /// \brief System object's default constructor.
102 ///
System(void)103 System::System (void):
104     m_ActiveState (0),
105     m_InvalidatedRegion (),
106     m_PreviousActiveState (0),
107     m_Screen (0),
108     m_ScreenScaleFactor (1.0f),
109     m_SoundEnabled (false),
110     m_States (0),
111     m_StatesToDelete (0),
112     m_UnicodeTranslationEnabled (false)
113 {
114 }
115 
116 ///
117 /// \brief System object's destructor.
118 ///
~System(void)119 System::~System (void)
120 {
121     // Deletes all states.
122     std::for_each (m_StatesToDelete.begin (), m_StatesToDelete.end (),
123                    DeleteObject<IState> ());
124     m_StatesToDelete.clear ();
125     std::for_each (m_States.begin (), m_States.end (),
126                    DeleteObject<IState> ());
127     m_States.clear ();
128     // Closes all opened joysticks.
129     std::for_each (m_Joysticks.begin (), m_Joysticks.end (),
130                    SDL_JoystickClose);
131     // Shuts down all SDL systems.
132     if ( m_SoundEnabled )
133     {
134         Mix_CloseAudio ();
135     }
136     SDL_Quit ();
137 }
138 
139 ///
140 /// \brief Applies the video mode in the options.
141 ///
142 void
applyVideoMode(void)143 System::applyVideoMode (void)
144 {
145     changeVideoMode ();
146 }
147 
148 ///
149 /// \brief Applies the volume level in the options.
150 ///
151 void
applyVolumeLevel(void)152 System::applyVolumeLevel (void)
153 {
154     if ( isSoundEnabled () )
155     {
156         Mix_Volume (-1, Options::getInstance ().getVolumeLevel () *
157                         MIX_MAX_VOLUME / Options::getMaxVolumeLevel ());
158         Mix_VolumeMusic (Options::getInstance ().getVolumeLevel () *
159                          MIX_MAX_VOLUME / Options::getMaxVolumeLevel ());
160     }
161 }
162 
163 ///
164 /// \brief Sets the new video mode and notifies all states.
165 ///
166 /// This function should be used when the video mode options (resolution,
167 /// full screen, etc...) are changed. It sets again the new video mode
168 /// and notifies all states (not only the active).
169 ///
170 void
changeVideoMode(void)171 System::changeVideoMode (void)
172 {
173     // Sets the new video mode from the options.
174     setVideoMode ();
175     // Notifies all states.
176     for ( std::vector<IState *>::iterator currentState = m_States.begin () ;
177           currentState < m_States.end () ; ++currentState )
178     {
179         (*currentState)->videoModeChanged ();
180     }
181     // Invalidates the whole screen.
182     invalidateWholeScreen ();
183 }
184 
185 ///
186 /// \brief Enables or disables unicode translations.
187 ///
188 /// \param enabled If set to \a true, the unicode translation of key pressed
189 ///                is activated and send through calls to
190 ///                IState::unicodeCharacterPressed().
191 ///
192 /// \note Enabling unicode translation can incur an overhead for each key
193 ///       pressed. So enable with care.
194 ///
195 void
enableUnicodeTranslation(bool enabled)196 System::enableUnicodeTranslation (bool enabled)
197 {
198     SDL_EnableUNICODE (enabled ? 1 : 0);
199     m_UnicodeTranslationEnabled = enabled;
200 }
201 
202 ///
203 /// \brief Gets the factor between the maxium screen resolution and the current.
204 ///
205 /// \return A factor between the maximum resolution and the current resolution.
206 ///
207 float
getScreenScaleFactor(void)208 System::getScreenScaleFactor (void)
209 {
210     return m_ScreenScaleFactor;
211 }
212 
213 ///
214 /// \brief Gets the SDL surface that represents the screen.
215 ///
216 /// \return The SDL surface of the screen.
217 ///
218 SDL_Surface *
getScreenSDLSurface(void)219 System::getScreenSDLSurface (void)
220 {
221     return m_Screen;
222 }
223 
224 ///
225 /// \brief Initializes the system.
226 ///
227 /// If anything goes wrong, this function throws a runtime_error exception.
228 ///
229 void
init(void)230 System::init (void)
231 {
232 #if defined (IS_OSX_HOST)
233     // Tell SDL to send key events to Cocoa, so shortcuts will work.
234     SDL_putenv ("SDL_ENABLEAPPEVENTS=1");
235 #endif // IS_OSX_HOST
236 
237 #if defined (IS_WIN32_HOST)
238     // When running under windows, set the current directory to be
239     // the executable directory.
240     changeToExecutableDirectory ();
241 #endif // IS_WIN32_HOST
242 
243     // Initialize pseudo-random generator.
244     srand ( (unsigned)time (0));
245 
246     // Initialize SDL.
247     if ( SDL_Init (SDL_INIT_AUDIO | SDL_INIT_JOYSTICK | SDL_INIT_VIDEO) < 0 )
248     {
249         throw std::runtime_error (SDL_GetError ());
250     }
251     // Sets the video mode from the configuration parameters.
252     setVideoMode ();
253     // Set the window's title for windowed modes.
254     SDL_WM_SetCaption (PACKAGE_NAME, PACKAGE_NAME);
255     // Set the maximum frame rate.
256     FrameManager::init (k_FrameRate);
257     // Open the audio device, if enabled.
258     if ( Options::getInstance ().isSoundEnabled () )
259     {
260         if ( Mix_OpenAudio (k_AudioRate, k_AudioFormat, k_AudioChannels,
261                             k_AudioBuffers) < 0 )
262         {
263             // Just print a warning, not being able to open a sound
264             // device is not a complete failure, the game is still playable.
265             std::cerr << "Couldn't open sound system: " << Mix_GetError ();
266             std::cerr << std::endl;
267         }
268         else
269         {
270             m_SoundEnabled = true;
271             applyVolumeLevel ();
272         }
273     }
274 
275 #if defined (IS_GP2X_HOST)
276     // When the program is launched from the GP2X main menu, it is possible
277     // that the SDL event queue registered the button down event of the
278     // "B" button. To prevent the game to think the user pressed the
279     // "B" button, I retrieve a single event from the event queue and
280     // just ignore it.
281     {
282         SDL_Event event;
283         SDL_PollEvent (&event);
284     }
285 
286     // Open the only joystic.
287     SDL_Joystick *joystick = SDL_JoystickOpen (0);
288     if ( NULL == joystick )
289     {
290         throw std::runtime_error (SDL_GetError ());
291     }
292     m_Joysticks.push_back (joystick);
293 
294     // Load the volume level display and gauge bar.
295     m_VolumeDisplay.reset (
296             Surface::fromFile (File::getGraphicsFilePath ("volume.png")));
297     m_VolumeLevel.reset (Surface::fromFile (
298                 File::getGraphicsFilePath ("volume-level.png")));
299     m_VolumeDisplayTime = 0;
300 #else // !IS_GP2X_HOST
301     int numJoysticks = SDL_NumJoysticks ();
302     for (int currentJoystick = 0 ; currentJoystick < numJoysticks ;
303          ++currentJoystick )
304     {
305         SDL_Joystick *joystick = SDL_JoystickOpen (currentJoystick);
306         if ( NULL != joystick )
307         {
308             // The joystick needs to have at least two axes and one
309             // button.
310             if ( 1 < SDL_JoystickNumAxes (joystick) &&
311                  0 < SDL_JoystickNumButtons (joystick) )
312             {
313                 m_Joysticks.push_back (joystick);
314             }
315         }
316     }
317 #endif // IS_GP2X_HOST
318 }
319 
320 ///
321 /// \brief Invalidates the whole screen.
322 ///
323 /// When the whole screen is invalidated, the state will redraw
324 /// the whole background.
325 ///
326 void
invalidateWholeScreen(void)327 System::invalidateWholeScreen (void)
328 {
329     SDL_Rect invalidatedRect;
330 
331     invalidatedRect.h = Options::getInstance ().getScreenHeight ();
332     invalidatedRect.w = Options::getInstance ().getScreenWidth ();
333     invalidatedRect.x = 0;
334     invalidatedRect.y = 0;
335     invalidateScreenRegion (&invalidatedRect);
336 }
337 
338 ///
339 /// \brief Tells if the video is in fullscreen mode.
340 ///
341 /// \return true if the mode video is in fullscreen and falss otherwise.
342 ///
343 bool
isFullScreen(void)344 System::isFullScreen (void)
345 {
346     return Options::getInstance().isFullScreen();
347 }
348 
349 ///
350 /// \brief Tells if the sound subsystem could be initialized.
351 ///
352 /// \return \a true if the sound is enabled, \a false otherwise.
353 ///
354 bool
isSoundEnabled(void)355 System::isSoundEnabled (void)
356 {
357     return m_SoundEnabled;
358 }
359 
360 ///
361 /// \brief Tells if unicode translation is enabled.
362 ///
363 /// \return \a true if unicode translation is enabled, \a false otherwise.
364 ///
365 bool
isUnicodeTranslationEnabled(void) const366 System::isUnicodeTranslationEnabled (void) const
367 {
368     return m_UnicodeTranslationEnabled;
369 }
370 
371 ///
372 /// \brief Sets a new PauseState as the active state.
373 ///
374 void
pause(void)375 System::pause (void)
376 {
377     if ( 0 != m_ActiveState && m_ActiveState->shouldBePaused () )
378     {
379         setActiveState (new PauseState (), FadeNone);
380     }
381 }
382 
383 ///
384 /// \brief Removes the active state from the stack.
385 ///
386 /// The state doesn't get deleted at this time, it's scheduled
387 /// to be deleted at the end of the current frame or at the end
388 /// of the system class.
389 ///
390 /// \param fadeOut Tells if add a fade out when the state is removed.
391 ///
392 void
removeActiveState(bool fadeOut)393 System::removeActiveState (bool fadeOut)
394 {
395     if ( !m_States.empty () )
396     {
397         IState *stateToDelete = m_States.back ();
398         // Remove the state from the active states list.
399         m_States.pop_back ();
400         // And add it to the list of states to delete at the end of the frame.
401         m_StatesToDelete.push_back (stateToDelete);
402         if ( fadeOut )
403         {
404             setActiveState (new FadeOutState (), FadeNone);
405         }
406     }
407 }
408 
409 ///
410 /// \brief Removes all states until the only remaining state is the main menu.
411 ///
412 /// The main menu should be the first state, since this function removes
413 /// all states until one is left.
414 ///
415 void
returnToMainMenu(void)416 System::returnToMainMenu (void)
417 {
418     while ( m_States.size () > 2 )
419     {
420         removeActiveState (false);
421     }
422     removeActiveState (true);
423 }
424 
425 ///
426 /// \brief Redraws the state's background.
427 ///
428 void
redrawStateBackground(void)429 System::redrawStateBackground (void)
430 {
431     if ( 0 != m_ActiveState &&
432          m_InvalidatedRegion.x1 < m_InvalidatedRegion.x2 &&
433          m_InvalidatedRegion.y1 < m_InvalidatedRegion.y2 )
434     {
435         SDL_Rect redrawRegion;
436         redrawRegion.h = m_InvalidatedRegion.y2 - m_InvalidatedRegion.y1;
437         redrawRegion.w = m_InvalidatedRegion.x2 - m_InvalidatedRegion.x1;
438         redrawRegion.x = m_InvalidatedRegion.x1;
439         redrawRegion.y = m_InvalidatedRegion.y1;
440 
441         m_ActiveState->redrawBackground (&redrawRegion, getScreenSDLSurface ());
442 
443         m_InvalidatedRegion.x1 = std::numeric_limits<int16_t>::max ();
444         m_InvalidatedRegion.y1 = std::numeric_limits<int16_t>::max ();
445         m_InvalidatedRegion.x2 = std::numeric_limits<int16_t>::min ();
446         m_InvalidatedRegion.y2 = std::numeric_limits<int16_t>::min ();
447     }
448 }
449 
450 ///
451 /// \brief System's main loop.
452 ///
453 /// This is the loop that drivers the whole system.
454 /// The call to this function doesn't return until the
455 /// system must shown down, in which case the application
456 /// can exit.
457 ///
458 void
run(void)459 System::run (void)
460 {
461     assert (0 != getScreenSDLSurface () &&
462             "Tried to run the system without initializing it.");
463     bool end = false;
464     do
465     {
466         // Get the active state or set it 0 if no state is active.
467         if ( m_States.empty () )
468         {
469             m_ActiveState = 0;
470         }
471         else
472         {
473             m_ActiveState = m_States.back ();
474         }
475 
476         // If we are going too fast, slow down so we won't burn the CPU.
477         FrameManager::update ();
478 
479         // If there's no active state, then we are done.
480         if ( 0 == m_ActiveState )
481         {
482             SDL_Event quitEvent;
483 
484             quitEvent.type = SDL_QUIT;
485             SDL_PushEvent (&quitEvent);
486         }
487         // If the current active state is different from the previously
488         // active state, then activate the current state.
489         else if ( m_ActiveState != m_PreviousActiveState )
490         {
491             m_ActiveState->activate ();
492             invalidateWholeScreen ();
493         }
494 
495         // Process all pending events.
496         SDL_Event event;
497         while ( SDL_PollEvent (&event) )
498         {
499             if ( SDL_QUIT == event.type )
500             {
501                 end = true;
502             }
503             else if ( 0 != m_ActiveState )
504             {
505                 switch ( event.type )
506                 {
507                     case SDL_JOYAXISMOTION:
508                         m_ActiveState->joyMotion (event.jaxis.which,
509                                                   event.jaxis.axis,
510                                                   event.jaxis.value);
511                         break;
512 
513                     case SDL_JOYBUTTONDOWN:
514 #if defined (IS_GP2X_HOST)
515                         if ( GP2X_BUTTON_VOLUP == event.jbutton.button )
516                         {
517                             Options::getInstance ().incrementVolume ();
518                             m_VolumeDisplayTime = k_VolumeDisplayTime;
519                             applyVolumeLevel ();
520                         }
521                         else if ( GP2X_BUTTON_VOLDOWN == event.jbutton.button )
522                         {
523                             Options::getInstance ().decrementVolume ();
524                             m_VolumeDisplayTime = k_VolumeDisplayTime;
525                             applyVolumeLevel ();
526                         }
527                         else
528 #endif  // IS_GP2X_HOST
529                         {
530                             m_ActiveState->joyDown (event.jbutton.which,
531                                                     event.jbutton.button);
532                         }
533                         break;
534 
535                     case SDL_JOYBUTTONUP:
536                         m_ActiveState->joyUp (event.jbutton.which,
537                                               event.jbutton.button);
538                         break;
539 
540 #if !defined (IS_GP2X_HOST)
541                     case SDL_ACTIVEEVENT:
542                         if ( event.active.state & SDL_APPACTIVE )
543                         {
544                             // Window is minimized.
545                             if ( 0 == event.active.gain )
546                             {
547                                 Music::pause ();
548                                 pause ();
549                             }
550                         }
551                         break;
552 
553                     case SDL_KEYDOWN:
554                         // ALT+RETURN is reserved to toggle full screen
555                         if ( SDLK_RETURN == event.key.keysym.sym &&
556                              (event.key.keysym.mod & KMOD_ALT) != 0 )
557                         {
558                             toggleFullScreen ();
559                         }
560                         else
561                         {
562                             m_ActiveState->keyDown (event.key.keysym.sym);
563                         }
564                         // Check if unicode translation is enabled
565                         // and send the unicode code if so.
566                         if ( isUnicodeTranslationEnabled () &&
567                              0 != event.key.keysym.unicode )
568                         {
569                             m_ActiveState->unicodeCharacterPressed (event.key.keysym.unicode);
570                         }
571                         break;
572 
573                     case SDL_KEYUP:
574                         m_ActiveState->keyUp (event.key.keysym.sym);
575                         break;
576 #endif // !IS_GP2X_HOST
577                 }
578             }
579         }
580 
581         // Update and render the active state.
582         if ( 0 != m_ActiveState )
583         {
584             // Update the state's logic.
585             m_ActiveState->update (FrameManager::getElapsedTime ());
586             // Remove any previously set clip rectangle.
587             SDL_SetClipRect (getScreenSDLSurface (), NULL);
588             // Draw the state's background for the invalided screen region.
589             redrawStateBackground ();
590             // Render the state.
591             m_ActiveState->render (getScreenSDLSurface ());
592         }
593 
594 #if defined (IS_GP2X_HOST)
595         // Show the current volume level if it has been changed.
596         if ( m_VolumeDisplayTime > 0 )
597         {
598             // Remove any previously set clip rectangle.
599             SDL_SetClipRect (getScreenSDLSurface (), NULL);
600 
601             // Show the display.
602             uint16_t x = Options::getInstance ().getScreenWidth () / 2 -
603                          m_VolumeDisplay->getWidth () / 2;
604             uint16_t y = Options::getInstance ().getScreenHeight () / 2 -
605                          m_VolumeDisplay->getHeight () / 2;
606             m_VolumeDisplay->blit (x, y, getScreenSDLSurface ());
607 
608             // Show the current level in white (upper part of the
609             // image) and the remaining up to the maximum level
610             // in gray (lower part of the image.)
611             //uint8_t volumeLevel = Options::getVolumeLevel ();
612             uint16_t width = m_VolumeLevel->getWidth () *
613                              Options::getInstance ().getVolumeLevel () /
614                              Options::getMaxVolumeLevel ();
615             if ( !isSoundEnabled () )
616             {
617                 width = 0;
618             }
619             x = Options::getInstance ().getScreenWidth () / 2 -
620                 m_VolumeLevel->getWidth () / 2;
621             y += m_VolumeDisplay->getHeight () - 10 -
622                  m_VolumeLevel->getHeight () / 2;
623             m_VolumeLevel->blit (0, 0,
624                                  width, m_VolumeLevel->getHeight () / 2,
625                                  x, y, getScreenSDLSurface ());
626 
627             x += width;
628             m_VolumeLevel->blit (width, m_VolumeLevel->getHeight () / 2,
629                                  m_VolumeLevel->getWidth () - width,
630                                  m_VolumeLevel->getHeight () / 2,
631                                  x, y, getScreenSDLSurface ());
632 
633             // Update the time to show the display.
634             m_VolumeDisplayTime -= FrameManager::getElapsedTime ();
635         }
636 #endif // IS_GP2X_HOST
637 
638         // Refresh the screen.
639         SDL_Flip (getScreenSDLSurface ());
640 
641         // Deletes the not longer used states.
642         if ( !m_StatesToDelete.empty () )
643         {
644             std::for_each (m_StatesToDelete.begin (), m_StatesToDelete.end (),
645                            DeleteObject<IState> ());
646             m_StatesToDelete.clear ();
647         }
648 
649         // Set the active state as the previous active state.
650         m_PreviousActiveState = m_ActiveState;
651     }
652     while ( !end );
653 }
654 
655 ///
656 /// \brief Sets the current active state.
657 ///
658 /// The System class takes ownership of the \a state class
659 /// and is responsable to delete it when no longer needed.
660 ///
661 /// \param state The state to set as the current active state.
662 /// \param fade Tells which type of fade to set when the state is active.
663 ///
664 void
setActiveState(IState * state,uint8_t fade)665 System::setActiveState (IState *state, uint8_t fade)
666 {
667     assert ( 0 != state && "Tried to set a NULL state as the active.");
668 
669     m_States.push_back (state);
670     if ( FadeIn == (fade & FadeIn) )
671     {
672         m_States.push_back (new FadeInState (state));
673     }
674     if ( FadeOut == (fade & FadeOut) )
675     {
676         m_States.push_back (new FadeOutState ());
677     }
678 }
679 
680 ///
681 /// \brief Sets the video mode reading the parameters from the options.
682 ///
683 void
setVideoMode(void)684 System::setVideoMode (void)
685 {
686     // Check if we should go to full screen.
687     uint32_t videoFlags = SDL_SWSURFACE;
688     if ( Options::getInstance ().isFullScreen () )
689     {
690         videoFlags |= SDL_FULLSCREEN;
691     }
692     // Sets the video mode.
693     m_Screen = SDL_SetVideoMode (Options::getInstance ().getScreenWidth (),
694                                  Options::getInstance ().getScreenHeight (),
695                                  Options::getInstance ().getScreenDepth (),
696                                  videoFlags);
697     if ( 0 == m_Screen )
698     {
699         // First check if the video settings are the defaults (which should
700         // be the most secure) and if they aren't, reset back to them and
701         // try again.
702         if ( !Options::getInstance ().isVideoOptionsAtDefault () )
703         {
704             Options::getInstance ().setDefaultVideoOptions ();
705             setVideoMode ();
706             // If the system was already initialized, then tell
707             // the user that her video settings couldn't be set.
708             if ( !m_States.empty () )
709             {
710                 setActiveState (new VideoErrorState (m_States.back ()),
711                                 FadeNone);
712             }
713         }
714         else
715         {
716             throw std::runtime_error (SDL_GetError ());
717         }
718     }
719     // Get the current screen's scale factor.
720     // Take into consideration the "odd" factor we need to have when
721     // using a 1024x768 screen resolution.
722     if ( 1024 == m_Screen->w )
723     {
724         m_ScreenScaleFactor = 0.75f;
725     }
726     else
727     {
728         m_ScreenScaleFactor = std::max (m_Screen->w / k_ScreenMaxWidth,
729                                         m_Screen->h / k_ScreenMaxHeight);
730     }
731     // Hide the cursor on the screen. Do only if the video mode is set
732     // to full screen, otherwise it feels awkward to make the cursor desappear
733     // when it passes over the window...
734     SDL_ShowCursor (Options::getInstance ().isFullScreen () ? SDL_DISABLE :
735                                                               SDL_ENABLE);
736 }
737 
738 ///
739 /// \brief Pressents a fatal error to the user.
740 ///
741 /// \param error The actual error message.
742 ///
743 void
showFatalError(const std::string & error)744 System::showFatalError (const std::string &error)
745 {
746     std::string title ("There was a fatal error an the application could not continue");
747 
748     std::ostringstream fullMessage;
749     fullMessage << "Reinstall the application and try again." << std::endl;
750     fullMessage << "If the error still occurs, please contact the" << std::endl;
751     fullMessage << "authors at <" << PACKAGE_BUGREPORT << "> ";
752     fullMessage << "attaching the following error message: " << std::endl;
753     fullMessage << std::endl << error << std::endl;
754 
755 #if defined (IS_WIN32_HOST)
756     MessageBox (NULL, (title + "\r\n" + fullMessage.str ()).c_str (),
757                 "Amoebax", MB_ICONERROR);
758 #elif defined (IS_OSX_HOST)
759     OSXAlert (title.c_str (), fullMessage.str ().c_str ());
760 #else
761     std::cerr << title << std::endl;
762     std::cerr << fullMessage.str () << std::endl;
763 #endif
764 }
765 
766 ///
767 /// \brief Toggles between full screen and windowed mode.
768 ///
769 void
toggleFullScreen(void)770 System::toggleFullScreen (void)
771 {
772     Options::getInstance ().setFullScreen (!Options::getInstance ().isFullScreen ());
773     changeVideoMode ();
774 }
775