b8d26ef335da3085f5f36fd448ff35850d314e4b
[supertux.git] / src / supertux / screen_manager.cpp
1 //  SuperTux
2 //  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
3 //
4 //  This program is free software: you can redistribute it and/or modify
5 //  it under the terms of the GNU General Public License as published by
6 //  the Free Software Foundation, either version 3 of the License, or
7 //  (at your option) any later version.
8 //
9 //  This program is distributed in the hope that it will be useful,
10 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
11 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 //  GNU General Public License for more details.
13 //
14 //  You should have received a copy of the GNU General Public License
15 //  along with this program.  If not, see <http://www.gnu.org/licenses/>.
16
17 #include "supertux/screen_manager.hpp"
18
19 #include "audio/sound_manager.hpp"
20 #include "control/input_manager.hpp"
21 #include "gui/menu.hpp"
22 #include "gui/menu_manager.hpp"
23 #include "scripting/squirrel_util.hpp"
24 #include "scripting/time_scheduler.hpp"
25 #include "supertux/console.hpp"
26 #include "supertux/constants.hpp"
27 #include "supertux/gameconfig.hpp"
28 #include "supertux/globals.hpp"
29 #include "supertux/main.hpp"
30 #include "supertux/menu/menu_storage.hpp"
31 #include "supertux/player_status.hpp"
32 #include "supertux/resources.hpp"
33 #include "supertux/screen.hpp"
34 #include "supertux/screen_fade.hpp"
35 #include "supertux/timer.hpp"
36 #include "video/drawing_context.hpp"
37 #include "video/renderer.hpp"
38
39 #include <stdio.h>
40 /** ticks (as returned from SDL_GetTicks) per frame */
41 static const Uint32 TICKS_PER_FRAME = (Uint32) (1000.0 / LOGICAL_FPS);
42 /** don't skip more than every 2nd frame */
43 static const int MAX_FRAME_SKIP = 2;
44
45 ScreenManager::ScreenManager() :
46   m_waiting_threads(),
47   m_menu_storage(new MenuStorage),
48   m_menu_manager(new MenuManager),
49   m_speed(1.0),
50   m_action(NO_ACTION),
51   m_fps(0),
52   m_next_screen(),
53   m_screen_fade(),
54   m_screen_stack(),
55   m_screenshot_requested(false)
56 {
57   using namespace scripting;
58   TimeScheduler::instance = new TimeScheduler();
59 }
60
61 ScreenManager::~ScreenManager()
62 {
63   using namespace scripting;
64   delete TimeScheduler::instance;
65   TimeScheduler::instance = NULL;
66 }
67
68 void
69 ScreenManager::push_screen(std::unique_ptr<Screen> screen, std::unique_ptr<ScreenFade> screen_fade)
70 {
71   log_debug << "ScreenManager::push_screen(): " << screen.get() << std::endl;
72   assert(screen);
73
74   if (m_action == PUSH_ACTION)
75   {
76     assert(m_next_screen);
77     // this happens for example when a Screen::setup() calls
78     // push_screen() itself, i.e. GameSessions/LevelIntro, in that
79     // case just commit the last action directly to the stack
80     m_screen_stack.push_back(std::move(m_next_screen));
81     m_action = NO_ACTION;
82   }
83
84   assert(m_action == NO_ACTION || m_action == POP_ACTION);
85
86   m_next_screen = std::move(screen);
87   m_screen_fade = std::move(screen_fade);
88
89   if (m_action == POP_ACTION)
90   {
91     m_action = REPLACE_ACTION;
92   }
93   else
94   {
95     m_action = PUSH_ACTION;
96   }
97   m_speed = 1.0f;
98 }
99
100 void
101 ScreenManager::pop_screen(std::unique_ptr<ScreenFade> screen_fade)
102 {
103   log_debug << "ScreenManager::pop_screen(): stack_size: " << m_screen_stack.size() << std::endl;
104   assert(m_action == NO_ACTION);
105
106   m_next_screen.reset();
107   m_screen_fade = std::move(screen_fade);
108   m_action = POP_ACTION;
109 }
110
111 void
112 ScreenManager::set_screen_fade(std::unique_ptr<ScreenFade> screen_fade)
113 {
114   m_screen_fade = std::move(screen_fade);
115 }
116
117 void
118 ScreenManager::quit(std::unique_ptr<ScreenFade> screen_fade)
119 {
120   m_screen_fade = std::move(screen_fade);
121   m_action = QUIT_ACTION;
122 }
123
124 void
125 ScreenManager::set_speed(float speed)
126 {
127   m_speed = speed;
128 }
129
130 float
131 ScreenManager::get_speed() const
132 {
133   return m_speed;
134 }
135
136 void
137 ScreenManager::draw_fps(DrawingContext& context, float fps_fps)
138 {
139   char str[60];
140   snprintf(str, sizeof(str), "%3.1f", fps_fps);
141   const char* fpstext = "FPS";
142   context.draw_text(Resources::small_font, fpstext,
143                     Vector(SCREEN_WIDTH - Resources::small_font->get_text_width(fpstext) - Resources::small_font->get_text_width(" 99999") - BORDER_X,
144                            BORDER_Y + 20), ALIGN_LEFT, LAYER_HUD);
145   context.draw_text(Resources::small_font, str, Vector(SCREEN_WIDTH - BORDER_X, BORDER_Y + 20), ALIGN_RIGHT, LAYER_HUD);
146 }
147
148 void
149 ScreenManager::draw(DrawingContext& context)
150 {
151   assert(!m_screen_stack.empty());
152
153   static Uint32 fps_ticks = SDL_GetTicks();
154   static int frame_count = 0;
155
156   m_screen_stack.back()->draw(context);
157   m_menu_manager->draw(context);
158
159   if (m_screen_fade)
160   {
161     m_screen_fade->draw(context);
162   }
163
164   Console::instance->draw(context);
165
166   if (g_config->show_fps)
167   {
168     draw_fps(context, m_fps);
169   }
170
171   // if a screenshot was requested, pass request on to drawing_context
172   if (m_screenshot_requested)
173   {
174     context.take_screenshot();
175     m_screenshot_requested = false;
176   }
177   context.do_drawing();
178
179   /* Calculate frames per second */
180   if (g_config->show_fps)
181   {
182     ++frame_count;
183
184     if (SDL_GetTicks() - fps_ticks >= 500)
185     {
186       m_fps = (float) frame_count / .5;
187       frame_count = 0;
188       fps_ticks = SDL_GetTicks();
189     }
190   }
191 }
192
193 void
194 ScreenManager::update_gamelogic(float elapsed_time)
195 {
196   scripting::update_debugger();
197   scripting::TimeScheduler::instance->update(game_time);
198
199   if (!m_screen_stack.empty())
200   {
201     m_screen_stack.back()->update(elapsed_time);
202   }
203
204   m_menu_manager->process_input();
205
206   if (m_screen_fade)
207   {
208     m_screen_fade->update(elapsed_time);
209   }
210
211   Console::instance->update(elapsed_time);
212 }
213
214 void
215 ScreenManager::process_events()
216 {
217   g_input_manager->update();
218   SDL_Event event;
219   while (SDL_PollEvent(&event))
220   {
221     g_input_manager->process_event(event);
222
223     m_menu_manager->event(event);
224
225     switch(event.type)
226     {
227       case SDL_QUIT:
228         quit();
229         break;
230
231       case SDL_WINDOWEVENT:
232         switch(event.window.event)
233         {
234           case SDL_WINDOWEVENT_RESIZED:
235             Renderer::instance()->resize(event.window.data1,
236                                          event.window.data2);
237             m_menu_manager->on_window_resize();
238             break;
239         }
240         break;
241
242       case SDL_KEYDOWN:
243         if (event.key.keysym.sym == SDLK_F10)
244         {
245           g_config->show_fps = !g_config->show_fps;
246         }
247         else if (event.key.keysym.sym == SDLK_F11)
248         {
249           g_config->use_fullscreen = !g_config->use_fullscreen;
250           Renderer::instance()->apply_config();
251           m_menu_manager->on_window_resize();
252         }
253         else if (event.key.keysym.sym == SDLK_PRINTSCREEN ||
254                  event.key.keysym.sym == SDLK_F12)
255         {
256           take_screenshot();
257         }
258         else if (event.key.keysym.sym == SDLK_F1 &&
259                  event.key.keysym.mod & KMOD_CTRL)
260         {
261           Console::instance->toggle();
262           g_config->console_enabled = true;
263           g_config->save();
264         }
265         break;
266     }
267   }
268 }
269
270 bool
271 ScreenManager::has_pending_fadeout() const
272 {
273   return m_screen_fade && !m_screen_fade->done();
274 }
275
276 void
277 ScreenManager::handle_screen_switch()
278 {
279   if (m_action != NO_ACTION && !has_pending_fadeout())
280   {
281     if (m_action == POP_ACTION)
282     {
283       assert(!m_screen_stack.empty());
284       m_action = NO_ACTION;
285
286       m_screen_stack.back()->leave();
287       m_screen_stack.pop_back();
288
289       if (!m_screen_stack.empty())
290       {
291         m_screen_stack.back()->setup();
292       }
293     }
294     else if (m_action == PUSH_ACTION)
295     {
296       assert(m_next_screen);
297       m_action = NO_ACTION;
298
299       if (!m_screen_stack.empty())
300       {
301         m_screen_stack.back()->leave();
302       }
303
304       m_screen_stack.push_back(std::move(m_next_screen));
305       m_screen_stack.back()->setup();
306     }
307     else if (m_action == REPLACE_ACTION)
308     {
309       assert(!m_screen_stack.empty());
310       assert(!m_next_screen);
311       m_action = NO_ACTION;
312
313       m_screen_stack.back()->leave();
314       m_screen_stack.pop_back();
315
316       m_screen_stack.push_back(std::move(m_next_screen));
317       m_screen_stack.back()->setup();
318     }
319     else if (m_action == QUIT_ACTION)
320     {
321       m_screen_stack.clear();
322     }
323
324     m_speed = 1.0;
325
326     m_screen_fade.reset();
327
328     m_waiting_threads.wakeup();
329   }
330 }
331
332 void
333 ScreenManager::run(DrawingContext &context)
334 {
335   Uint32 last_ticks = 0;
336   Uint32 elapsed_ticks = 0;
337
338   assert(m_action == PUSH_ACTION);
339   m_screen_stack.push_back(std::move(m_next_screen));
340   m_action = NO_ACTION;
341
342   while (!m_screen_stack.empty())
343   {
344     Uint32 ticks = SDL_GetTicks();
345     elapsed_ticks += ticks - last_ticks;
346     last_ticks = ticks;
347
348     Uint32 ticks_per_frame = (Uint32) (TICKS_PER_FRAME * g_game_speed);
349
350     if (elapsed_ticks > ticks_per_frame*4)
351     {
352       // when the game loads up or levels are switched the
353       // elapsed_ticks grows extremely large, so we just ignore those
354       // large time jumps
355       elapsed_ticks = 0;
356     }
357
358     if (elapsed_ticks < ticks_per_frame)
359     {
360       Uint32 delay_ticks = ticks_per_frame - elapsed_ticks;
361       SDL_Delay(delay_ticks);
362       last_ticks += delay_ticks;
363       elapsed_ticks += delay_ticks;
364     }
365
366     int frames = 0;
367
368     while (elapsed_ticks >= ticks_per_frame && frames < MAX_FRAME_SKIP)
369     {
370       elapsed_ticks -= ticks_per_frame;
371       float timestep = 1.0 / LOGICAL_FPS;
372       real_time += timestep;
373       timestep *= m_speed;
374       game_time += timestep;
375
376       process_events();
377       update_gamelogic(timestep);
378       frames += 1;
379     }
380
381     if (!m_screen_stack.empty())
382     {
383       draw(context);
384     }
385
386     sound_manager->update();
387
388     handle_screen_switch();
389   }
390 }
391
392 void
393 ScreenManager::take_screenshot()
394 {
395   m_screenshot_requested = true;
396 }
397
398 /* EOF */