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