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