The Battle for Wesnoth  1.17.0-dev
loading_screen.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2016 - 2021
3  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
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 2 of the License, or
8  (at your option) any later version.
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY.
11 
12  See the COPYING file for more details.
13 */
14 
15 /**
16  * @file
17  * Screen with logo and loading status info during program-startup.
18  */
19 
20 #define GETTEXT_DOMAIN "wesnoth-lib"
21 
23 
24 #include "cursor.hpp"
25 #include "gettext.hpp"
27 #include "gui/core/timer.hpp"
28 #include "gui/widgets/drawing.hpp"
29 #include "gui/widgets/label.hpp"
30 #include "gui/widgets/settings.hpp"
31 #include "gui/widgets/window.hpp"
32 #include "log.hpp"
33 #include "preferences/general.hpp"
34 #include "video.hpp"
35 
36 #include <cstdlib>
37 #include <functional>
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::dialogs
70 {
71 REGISTER_DIALOG(loading_screen)
72 
73 loading_screen* loading_screen::singleton_ = nullptr;
74 
75 loading_screen::loading_screen(std::function<void()> f)
76  : load_func_(f)
77  , worker_result_()
78  , cursor_setter_()
79  , progress_stage_label_(nullptr)
80  , animation_(nullptr)
81  , animation_start_()
82  , current_stage_(loading_stage::none)
83  , visible_stages_()
84  , current_visible_stage_()
85 {
86  for(const auto& [stage, description] : stage_names) {
87  visible_stages_[stage] = t_string(description, "wesnoth-lib") + "...";
88  }
89 
91  singleton_ = this;
92 }
93 
95 {
96  window.set_enter_disabled(true);
97  window.set_escape_disabled(true);
98 
100 
101  if(load_func_) {
102  // Run the load function in its own thread.
103  try {
104  worker_result_ = std::async(std::launch::async, load_func_);
105  } catch(const std::system_error& e) {
106  ERR_LS << "Failed to create worker thread: " << e.what() << "\n";
107  throw;
108  }
109  }
110 
111  progress_stage_label_ = find_widget<label>(&window, "status", false, true);
112  animation_ = find_widget<drawing>(&window, "animation", false, true);
113 
114  // Add a draw callback to handle the animation, et al.
115  window.connect_signal<event::DRAW>(
117 }
118 
120 {
121  cursor_setter_.reset();
122 }
123 
125 {
126  if(singleton_ && stage != loading_stage::none) {
127  singleton_->current_stage_.store(stage, std::memory_order_release);
128  }
129 }
130 
132 {
133  using namespace std::chrono_literals;
134 
135  if(!load_func_ || worker_result_.wait_for(0ms) == std::future_status::ready) {
136  // The worker returns void, so this is only to handle any exceptions thrown from the worker.
137  // worker_result_.valid() will return false after.
138  if(worker_result_.valid()) {
139  worker_result_.get();
140  }
141 
142  get_window()->close();
143  }
144 }
145 
147 {
148  loading_stage stage = current_stage_.load(std::memory_order_acquire);
149 
150  if(stage != loading_stage::none && (current_visible_stage_ == visible_stages_.end() || stage != current_visible_stage_->first)) {
151  auto iter = visible_stages_.find(stage);
152  if(iter == visible_stages_.end()) {
153  WRN_LS << "Stage missing description." << std::endl;
154  return;
155  }
156 
157  current_visible_stage_ = iter;
158  progress_stage_label_->set_label(iter->second);
159  }
160 
161  using namespace std::chrono;
162  const auto now = steady_clock::now();
163 
164  // We only need to set the start time once;
165  if(!animation_start_.has_value()) {
166  animation_start_ = now;
167  }
168 
169  animation_->get_drawing_canvas().set_variable("time", wfl::variant(duration_cast<milliseconds>(now - *animation_start_).count()));
170  animation_->set_is_dirty(true);
171 }
172 
174 {
175  /* If the worker thread is running, exit the application to prevent memory corruption.
176  * TODO: this is still not optimal. The main problem is that this code assumes that this
177  * happened because the window was closed, which is not necessarily the case (other
178  * possibilities might be a 'dialog doesn't fit on screen' exception caused by resizing
179  * the window).
180  */
181  if(worker_result_.valid()) {
182 #if defined(_LIBCPP_VERSION) || defined(__MINGW32__)
183  std::_Exit(0);
184 #else
185  std::quick_exit(0);
186 #endif
187  }
188 
189  singleton_ = nullptr;
190 }
191 
192 void loading_screen::display(std::function<void()> f)
193 {
194  if(singleton_ || CVideo::get_singleton().faked()) {
195  f();
196  } else {
197  loading_screen(f).show();
198  }
199 }
200 
201 } // namespace dialogs
virtual void pre_show(window &window) override
Actions to be taken before showing the window.
static lg::log_domain log_loadscreen("loadscreen")
#define WRN_LS
void close()
Requests to close the window.
Definition: window.hpp:182
stage_map::const_iterator current_visible_stage_
void set_variable(const std::string &key, wfl::variant &&value)
Definition: canvas.hpp:168
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:49
void set_escape_disabled(const bool escape_disabled)
Disable the escape key.
Definition: window.hpp:299
window * get_window() const
Returns a pointer to the dialog&#39;s window.
#define ERR_LS
loading_stage
Loading screen stage IDs.
virtual void set_label(const t_string &label)
static const std::map< loading_stage, std::string > stage_names
This file contains the settings handling of the widget library.
void set_is_dirty(const bool is_dirty)
Definition: widget.cpp:466
Periodic redraw request.
Definition: handler.hpp:50
canvas & get_drawing_canvas()
Definition: drawing.hpp:57
std::future< void > worker_result_
virtual void process(events::pump_info &) override
Inherited from events::pump_monitor.
std::optional< decltype(std::chrono::steady_clock::now())> animation_start_
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:506
virtual void post_show(window &window) override
Actions to be taken after the window has been shown.
Contains the gui2 timer routines.
void draw_callback()
Callback to handle drawing the progress animation.
#define N_(String)
Definition: gettext.hpp:101
std::unique_ptr< cursor::setter > cursor_setter_
static loading_screen * singleton_
static void display(std::function< void()> f)
#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:65
void set_enter_disabled(const bool enter_disabled)
Disable the enter key.
Definition: window.hpp:286