The Battle for Wesnoth  1.13.11+dev
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
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 <boost/thread.hpp>
36 
37 #include <cstdlib>
38 
39 #if defined(_MSC_VER) && _MSC_VER <= 1800
40 #include <Windows.h>
41 #endif
42 
43 static lg::log_domain log_loadscreen("loadscreen");
44 #define ERR_LS LOG_STREAM(err, log_loadscreen)
45 #define WRN_LS LOG_STREAM(warn, log_loadscreen)
46 #define LOG_LS LOG_STREAM(info, log_loadscreen)
47 #define DBG_LS LOG_STREAM(debug, log_loadscreen)
48 
49 static const std::map<loading_stage, std::string> stage_names {
50  { loading_stage::build_terrain, N_("Building terrain rules") },
51  { loading_stage::create_cache, N_("Reading files and creating cache") },
52  { loading_stage::init_display, N_("Initializing display") },
53  { loading_stage::init_fonts, N_("Reinitialize fonts for the current language") },
54  { loading_stage::init_teams, N_("Initializing teams") },
55  { loading_stage::init_theme, N_("Initializing display") },
56  { loading_stage::load_config, N_("Loading game configuration") },
57  { loading_stage::load_data, N_("Loading data files") },
58  { loading_stage::load_level, N_("Loading level") },
59  { loading_stage::init_lua, N_("Initializing scripting engine") },
60  { loading_stage::init_whiteboard, N_("Initializing planning mode") },
61  { loading_stage::load_unit_types, N_("Reading unit files") },
62  { loading_stage::load_units, N_("Loading units") },
63  { loading_stage::refresh_addons, N_("Searching for installed add-ons") },
64  { loading_stage::start_game, N_("Starting game") },
65  { loading_stage::verify_cache, N_("Verifying cache") },
66  { loading_stage::connect_to_server, N_("Connecting to server") },
67  { loading_stage::login_response, N_("Logging in") },
68  { loading_stage::waiting, N_("Waiting for server") },
69  { loading_stage::redirect, N_("Connecting to redirected server") },
70  { loading_stage::next_scenario, N_("Waiting for next scenario") },
71  { loading_stage::download_level_data, N_("Getting game data") },
72  { loading_stage::download_lobby_data, N_("Downloading lobby data") },
73 };
74 
75 namespace gui2
76 {
77 namespace dialogs
78 {
79 REGISTER_DIALOG(loading_screen)
80 
81 loading_screen::loading_screen(std::function<void()> f)
82  : timer_id_(0)
83  , animation_counter_(0)
84  , work_(f)
85  , worker_()
86  , cursor_setter_()
87  , progress_stage_label_(nullptr)
88  , animation_label_(nullptr)
89  , current_stage_(loading_stage::none)
90  , visible_stages_()
91  , animation_stages_()
92  , current_visible_stage_()
93  , is_worker_running_(false)
94 {
95  for(const auto& pair : stage_names) {
96  visible_stages_[pair.first] = t_string(pair.second, "wesnoth-lib") + "...";
97  }
98 
99  for(int i = 0; i != 20; ++i) {
100  std::string s(20, ' ');
101  s[i] = '.';
102  animation_stages_.push_back(s);
103  }
104 
105  current_visible_stage_ = visible_stages_.end();
106  current_load = this;
107 }
108 
110 {
111  if(work_) {
112  worker_.reset(new boost::thread([this]() {
113  is_worker_running_ = true;
114 
115  try {
116  work_();
117  } catch(...) {
118  // TODO: guard this with a mutex.
119  exception_ = std::current_exception();
120  }
121 
122  is_worker_running_ = false;
123  }));
124  }
125 
126  timer_id_ = add_timer(100, std::bind(&loading_screen::timer_callback, this, std::ref(window)), true);
128  progress_stage_label_ = find_widget<label>(&window, "status", false, true);
129  animation_label_ = find_widget<label>(&window, "test_animation", false, true);
130 
131  window.set_enter_disabled(true);
132  window.set_escape_disabled(true);
133 }
134 
136 {
137  worker_.reset();
138  clear_timer();
139  cursor_setter_.reset();
140 }
141 
143 {
144  if(!current_load) {
145  return;
146  }
147 
148  if(stage != loading_stage::none) {
149  current_load->current_stage_.store(stage, std::memory_order_release);
150  }
151 }
152 
154 
156 {
157  if(!work_ || !worker_ || worker_->timed_join(boost::posix_time::milliseconds(0))) {
158  if(exception_) {
159  clear_timer();
160  std::rethrow_exception(exception_);
161  }
162 
163  window.close();
164  }
165 
166  if(!work_) {
167  return;
168  }
169 
170  loading_stage stage = current_stage_.load(std::memory_order_acquire);
171 
172  if(stage != loading_stage::none && (current_visible_stage_ == visible_stages_.end() || stage != current_visible_stage_->first)) {
173  auto iter = visible_stages_.find(stage);
174  if(iter == visible_stages_.end()) {
175  WRN_LS << "Stage missing description." << std::endl;
176  return;
177  }
178 
179  current_visible_stage_ = iter;
180  progress_stage_label_->set_label(iter->second);
181  }
182 
184  if(animation_counter_ % 2 == 0) {
186  }
187 }
188 
190 {
191  /* If the worker thread is running, exit the application to prevent memory corruption.
192  * TODO: this is still not optimal. The main problem is that this code assumes that this
193  * happened because the window was closed, which is not necessarily the case (other
194  * possibilities might be a 'dialog doesn't fit on screen' exception caused by resizing
195  * the window).
196  *
197  * Another approach might be to add exit points (boost::this_thread::interruption_point())
198  * to the worker functions (filesystem.cpp, config parsing code, etc.) and then use that
199  * to end the thread faster.
200  */
201  if(is_worker_running_) {
202 #if defined(_MSC_VER) && _MSC_VER <= 1800
203  HANDLE process = GetCurrentProcess();
204  TerminateProcess(process, 0u);
205 #elif defined(_LIBCPP_VERSION) || defined(__MINGW32__)
206  std::_Exit(0);
207 #else
208  std::quick_exit(0);
209 #endif
210  }
211 
212  clear_timer();
213  current_load = nullptr;
214 }
215 
216 void loading_screen::display(std::function<void()> f)
217 {
218  const bool use_loadingscreen_animation = !preferences::disable_loadingscreen_animation();
219 
220  if(current_load || CVideo::get_singleton().faked()) {
221  f();
222  } else if(use_loadingscreen_animation) {
223  loading_screen(f).show();
224  } else {
225  loading_screen(std::function<void()>()).show();
226  f();
227  }
228 }
229 
231 {
232  if(timer_id_ != 0) {
234  timer_id_ = 0;
235  }
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:211
std::vector< char_t > string
stage_map::const_iterator current_visible_stage_
std::atomic< loading_stage > current_stage_
size_t add_timer(const uint32_t interval, const std::function< void(size_t id)> &callback, const bool repeat)
Adds a new timer.
Definition: timer.cpp:111
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)
bool remove_timer(const size_t id)
Removes a timer.
Definition: timer.cpp:143
static CVideo & get_singleton()
Definition: video.hpp:43
STL namespace.
std::function< void()> work_
-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_
void process(int mousex, int mousey)
Definition: tooltips.cpp:195
static const std::map< loading_stage, std::string > stage_names
This file contains the settings handling of the widget library.
Various uncategorised dialogs.
static map_location::DIRECTION s
virtual void post_show(window &window) override
Inherited from modal_dialog.
std::unique_ptr< boost::thread > worker_
size_t i
Definition: function.cpp:933
Contains the gui2 timer routines.
#define N_(String)
Definition: gettext.hpp:97
std::unique_ptr< cursor::setter > cursor_setter_
bool disable_loadingscreen_animation()
Definition: general.cpp:1059
static void display(std::function< void()> f)
static loading_screen * current_load
#define f
Standard logging facilities (interface).
void timer_callback(window &window)
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:62