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