The Battle for Wesnoth  1.15.0-dev
loading_screen.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2016 - 2018 by the Battle for Wesnoth Project http://www.wesnoth.org/
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 2 of the License, or
7  (at your option) any later version.
8  This program is distributed in the hope that it will be useful,
9  but WITHOUT ANY WARRANTY.
10 
11  See the COPYING file for more details.
12 */
13 
14 /**
15  * @file
16  * Screen with logo and loading status info during program-startup.
17  */
18 
19 #define GETTEXT_DOMAIN "wesnoth-lib"
20 
22 
23 #include "cursor.hpp"
24 #include "gettext.hpp"
26 #include "gui/core/timer.hpp"
27 #include "gui/widgets/label.hpp"
28 #include "gui/widgets/settings.hpp"
29 #include "gui/widgets/window.hpp"
30 #include "log.hpp"
31 #include "preferences/general.hpp"
32 #include "utils/functional.hpp"
33 #include "video.hpp"
34 
35 #include <chrono>
36 #include <cstdlib>
37 #include <limits>
38 
39 static lg::log_domain log_loadscreen("loadscreen");
40 #define ERR_LS LOG_STREAM(err, log_loadscreen)
41 #define WRN_LS LOG_STREAM(warn, log_loadscreen)
42 
43 static const std::map<loading_stage, std::string> stage_names {
44  { loading_stage::build_terrain, N_("Building terrain rules") },
45  { loading_stage::create_cache, N_("Reading files and creating cache") },
46  { loading_stage::init_display, N_("Initializing display") },
47  { loading_stage::init_fonts, N_("Reinitialize fonts for the current language") },
48  { loading_stage::init_teams, N_("Initializing teams") },
49  { loading_stage::init_theme, N_("Initializing display") },
50  { loading_stage::load_config, N_("Loading game configuration") },
51  { loading_stage::load_data, N_("Loading data files") },
52  { loading_stage::load_level, N_("Loading level") },
53  { loading_stage::init_lua, N_("Initializing scripting engine") },
54  { loading_stage::init_whiteboard, N_("Initializing planning mode") },
55  { loading_stage::load_unit_types, N_("Reading unit files") },
56  { loading_stage::load_units, N_("Loading units") },
57  { loading_stage::refresh_addons, N_("Searching for installed add-ons") },
58  { loading_stage::start_game, N_("Starting game") },
59  { loading_stage::verify_cache, N_("Verifying cache") },
60  { loading_stage::connect_to_server, N_("Connecting to server") },
61  { loading_stage::login_response, N_("Logging in") },
62  { loading_stage::waiting, N_("Waiting for server") },
63  { loading_stage::redirect, N_("Connecting to redirected server") },
64  { loading_stage::next_scenario, N_("Waiting for next scenario") },
65  { loading_stage::download_level_data, N_("Getting game data") },
66  { loading_stage::download_lobby_data, N_("Downloading lobby data") },
67 };
68 
69 namespace gui2
70 {
71 namespace dialogs
72 {
73 REGISTER_DIALOG(loading_screen)
74 
75 loading_screen* loading_screen::current_load = nullptr;
76 
77 loading_screen::loading_screen(std::function<void()> f)
78  : animation_counter_(0)
79  , next_animation_time_(std::numeric_limits<uint32_t>::max())
80  , load_func_(f)
81  , worker_result_()
82  , cursor_setter_()
83  , progress_stage_label_(nullptr)
84  , animation_label_(nullptr)
85  , current_stage_(loading_stage::none)
86  , visible_stages_()
87  , animation_stages_()
88  , current_visible_stage_()
89 {
90  for(const auto& pair : stage_names) {
91  visible_stages_[pair.first] = t_string(pair.second, "wesnoth-lib") + "...";
92  }
93 
94  animation_stages_.reserve(20);
95 
96  for(int i = 0; i != 20; ++i) {
97  std::string s(20, ' ');
98  s[i] = '.';
99  animation_stages_.push_back(std::move(s));
100  }
101 
103  current_load = this;
104 }
105 
107 {
108  window.set_enter_disabled(true);
109  window.set_escape_disabled(true);
110 
112 
113  if(load_func_) {
114  // Run the load function in its own thread.
115  try {
116  worker_result_ = std::async(std::launch::async, [this]() {
117  try {
118  load_func_();
119  } catch(...) {
120  // TODO: guard this with a mutex.
121  exception_ = std::current_exception();
122  }
123  });
124  } catch(const std::system_error& e) {
125  ERR_LS << "Failed to create worker thread: " << e.what() << "\n";
126  throw;
127  }
128  }
129 
130  progress_stage_label_ = find_widget<label>(&window, "status", false, true);
131  animation_label_ = find_widget<label>(&window, "test_animation", false, true);
132 
133  // Add a draw callback to handle the animation, et al.
134  window.connect_signal<event::DRAW>(
136 
138 }
139 
141 {
142  cursor_setter_.reset();
143 }
144 
146 {
147  if(current_load && stage != loading_stage::none) {
148  current_load->current_stage_.store(stage, std::memory_order_release);
149  }
150 }
151 
153 {
154  if(!load_func_ || loading_complete()) {
155  if(exception_) {
156  std::rethrow_exception(exception_);
157  }
158 
159  get_window()->close();
160  }
161 }
162 
164 {
165  loading_stage stage = current_stage_.load(std::memory_order_acquire);
166 
167  if(stage != loading_stage::none && (current_visible_stage_ == visible_stages_.end() || stage != current_visible_stage_->first)) {
168  auto iter = visible_stages_.find(stage);
169  if(iter == visible_stages_.end()) {
170  WRN_LS << "Stage missing description." << std::endl;
171  return;
172  }
173 
174  current_visible_stage_ = iter;
175  progress_stage_label_->set_label(iter->second);
176  }
177 
178  //if(SDL_GetTicks() < next_animation_time_) {
179  // return;
180  //}
181 
183  if(animation_counter_ % 2 == 0) {
185  }
186 
187  //set_next_animation_time();
188 }
189 
191 {
192  /* If the worker thread is running, exit the application to prevent memory corruption.
193  * TODO: this is still not optimal. The main problem is that this code assumes that this
194  * happened because the window was closed, which is not necessarily the case (other
195  * possibilities might be a 'dialog doesn't fit on screen' exception caused by resizing
196  * the window).
197  *
198  * Another approach might be to add exit points (boost::this_thread::interruption_point())
199  * to the worker functions (filesystem.cpp, config parsing code, etc.) and then use that
200  * to end the thread faster.
201  */
202  if(!loading_complete()) {
203 #if defined(_LIBCPP_VERSION) || defined(__MINGW32__)
204  std::_Exit(0);
205 #else
206  std::quick_exit(0);
207 #endif
208  }
209 
210  current_load = nullptr;
211 }
212 
213 void loading_screen::display(std::function<void()> f)
214 {
215  const bool use_loadingscreen_animation = !preferences::disable_loadingscreen_animation();
216 
217  if(current_load || CVideo::get_singleton().faked()) {
218  f();
219  } else if(use_loadingscreen_animation) {
220  loading_screen(f).show();
221  } else {
222  loading_screen(std::function<void()>()).show();
223  f();
224  }
225 }
226 
228 {
229  using namespace std::chrono_literals;
230  return worker_result_.wait_for(0ms) == std::future_status::ready;
231 }
232 
234 {
235  next_animation_time_ = SDL_GetTicks() + 100;
236 }
237 
238 } // namespace dialogs
239 } // namespace gui2
virtual void pre_show(window &window) override
Inherited from modal_dialog.
static lg::log_domain log_loadscreen("loadscreen")
#define WRN_LS
void close()
Requests to close the window.
Definition: window.hpp:165
stage_map::const_iterator current_visible_stage_
std::atomic< loading_stage > current_stage_
This file contains the window object, this object is a top level container which has the event manage...
static void progress(loading_stage stage=loading_stage::none)
static CVideo & get_singleton()
Definition: video.hpp:48
void set_escape_disabled(const bool escape_disabled)
Disable the escape key.
Definition: window.hpp:282
STL namespace.
window * get_window() const
Returns a pointer to the dialog&#39;s window.
#define ERR_LS
-file util.hpp
Generic file dialog.
Definition: field-fwd.hpp:22
loading_stage
Loading screen stage IDs.
virtual void set_label(const t_string &label)
std::vector< t_string > animation_stages_
static const std::map< loading_stage, std::string > stage_names
This file contains the settings handling of the widget library.
Periodic redraw request.
Definition: handler.hpp:49
std::future< void > worker_result_
bool loading_complete() const
Checks whether the worker thread has returned and loading is complete.
Various uncategorised dialogs.
virtual void process(events::pump_info &) override
Inherited from events::pump_monitor.
std::size_t i
Definition: function.cpp:933
std::enable_if_t< is_general_event(E)> connect_signal(const signal_function &signal, const queue_position position=back_child)
Connect a signal for callback in set_event.
Definition: dispatcher.hpp:483
static map_location::DIRECTION s
virtual void post_show(window &window) override
Inherited from modal_dialog.
Contains the gui2 timer routines.
void draw_callback()
Callback to handle drawing the progress animation.
#define N_(String)
Definition: gettext.hpp:97
std::unique_ptr< cursor::setter > cursor_setter_
bool disable_loadingscreen_animation()
Definition: general.cpp:921
static void display(std::function< void()> f)
static loading_screen * current_load
#define f
std::function< void()> load_func_
Standard logging facilities (interface).
#define e
loading_screen(std::function< void()> f)
base class of top level items, the only item which needs to store the final canvases to draw on ...
Definition: window.hpp:63
void set_enter_disabled(const bool enter_disabled)
Disable the enter key.
Definition: window.hpp:269