The Battle for Wesnoth  1.17.21+dev
loading_screen.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2016 - 2023
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 "draw_manager.hpp"
26 #include "gettext.hpp"
28 #include "gui/core/timer.hpp"
29 #include "gui/widgets/drawing.hpp"
30 #include "gui/widgets/label.hpp"
31 #include "gui/widgets/settings.hpp"
32 #include "gui/widgets/window.hpp"
33 #include "log.hpp"
34 #include "preferences/general.hpp"
35 #include "sdl/rect.hpp"
36 #include "video.hpp"
37 
38 #include <cstdlib>
39 #include <functional>
40 
41 static lg::log_domain log_loadscreen("loadscreen");
42 #define LOG_LS LOG_STREAM(info, log_loadscreen)
43 #define ERR_LS LOG_STREAM(err, log_loadscreen)
44 #define WRN_LS LOG_STREAM(warn, log_loadscreen)
45 
46 static lg::log_domain log_display("display");
47 #define DBG_DP LOG_STREAM(debug, log_display)
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 { int last_spin_ = 0; }
76 
77 namespace gui2::dialogs
78 {
79 REGISTER_DIALOG(loading_screen)
80 
81 loading_screen* loading_screen::singleton_ = nullptr;
82 
83 loading_screen::loading_screen(std::function<void()> f)
84  : modal_dialog(window_id())
85  , load_funcs_{f}
86  , worker_result_()
87  , cursor_setter_()
88  , progress_stage_label_(nullptr)
89  , animation_(nullptr)
90  , animation_start_()
91  , current_stage_(loading_stage::none)
92  , visible_stages_()
93  , current_visible_stage_()
94  , running_(false)
95 {
96  for(const auto& [stage, description] : stage_names) {
97  visible_stages_[stage] = t_string(description, "wesnoth-lib") + "...";
98  }
99 
101  singleton_ = this;
102 }
103 
105 {
108 
110 
111  progress_stage_label_ = find_widget<label>(&window, "status", false, true);
112  animation_ = find_widget<drawing>(&window, "animation", false, true);
113 }
114 
116 {
117  cursor_setter_.reset();
118 }
119 
121 {
122  if(singleton_ && stage != loading_stage::none) {
123  singleton_->current_stage_.store(stage, std::memory_order_release);
124  // Allow display to update, close events to be handled, etc.
126  }
127 }
128 
130 {
131  // If we're not showing a loading screen, do nothing.
132  if (!singleton_) {
133  return;
134  }
135 
136  // If we're not the main thread, do nothing.
137  if (!events::is_in_main_thread()) {
138  return;
139  }
140 
141  // Restrict actual update rate.
142  int elapsed = SDL_GetTicks() - last_spin_;
143  if (elapsed > draw_manager::get_frame_length() || elapsed < 0) {
144  last_spin_ = SDL_GetTicks();
146  }
147 }
148 
150 {
151  if (singleton_) {
153  }
154 }
155 
156 // This will be run inside the window::show() loop.
158 {
159  if (load_funcs_.empty()) {
160  return;
161  }
162 
163  // Do not automatically recurse.
164  if (running_) { return; }
165  running_ = true;
166 
167  // Run the loading function.
168  auto func = load_funcs_.back();
169  load_funcs_.pop_back();
170  LOG_LS << "Executing loading screen worker function.";
171  func();
172 
173  running_ = false;
174 
175  // If there's nothing more to do, close.
176  if (load_funcs_.empty()) {
177  queue_redraw();
178  window::close();
179  }
180 }
181 
183 {
185 
186  DBG_DP << "loading_screen::layout";
187 
188  loading_stage stage = current_stage_.load(std::memory_order_acquire);
189 
190  if(stage != loading_stage::none && (current_visible_stage_ == visible_stages_.end() || stage != current_visible_stage_->first)) {
191  auto iter = visible_stages_.find(stage);
192  if(iter == visible_stages_.end()) {
193  WRN_LS << "Stage missing description.";
194  return;
195  }
196 
197  current_visible_stage_ = iter;
198  progress_stage_label_->set_label(iter->second);
199  }
200 
201  using namespace std::chrono;
202  const auto now = steady_clock::now();
203 
204  // We only need to set the start time once;
205  if(!animation_start_.has_value()) {
206  animation_start_ = now;
207  }
208 
209  animation_->get_drawing_canvas().set_variable("time", wfl::variant(duration_cast<milliseconds>(now - *animation_start_).count()));
211 }
212 
214 {
215  LOG_LS << "Loading screen destroyed.";
216  singleton_ = nullptr;
217 }
218 
219 void loading_screen::display(std::function<void()> f)
220 {
221  if(singleton_ || video::headless()) {
222  LOG_LS << "Directly executing loading function.";
223  f();
224  } else {
225  LOG_LS << "Creating new loading screen.";
226  loading_screen(f).show();
227  }
228 }
229 
230 } // namespace dialogs
void set_variable(const std::string &key, wfl::variant &&value)
Definition: canvas.hpp:137
static void progress(loading_stage stage=loading_stage::none)
Report what is being loaded to the loading screen.
std::optional< decltype(std::chrono::steady_clock::now())> animation_start_
virtual void layout() override
Called by draw_manager to assign concrete layout.
std::atomic< loading_stage > current_stage_
static loading_screen * singleton_
virtual void process(events::pump_info &) override
Inherited from events::pump_monitor.
static void raise()
Raise the loading screen to the top of the draw stack.
static void display(std::function< void()> f)
virtual void post_show(window &window) override
Actions to be taken after the window has been shown.
std::vector< std::function< void()> > load_funcs_
std::unique_ptr< cursor::setter > cursor_setter_
virtual void pre_show(window &window) override
Actions to be taken before showing the window.
stage_map::const_iterator current_visible_stage_
loading_screen(std::function< void()> f)
static void spin()
Indicate to the player that loading is progressing.
Abstract base class for all modal dialogs.
canvas & get_drawing_canvas()
Definition: drawing.hpp:57
virtual void set_label(const t_string &label)
void queue_redraw()
Indicates that this widget should be redrawn.
Definition: widget.cpp:442
base class of top level items, the only item which needs to store the final canvases to draw on.
Definition: window.hpp:67
void set_enter_disabled(const bool enter_disabled)
Disable the enter key.
Definition: window.hpp:286
void close()
Requests to close the window.
Definition: window.hpp:182
virtual void layout() override
Lays out the window.
Definition: window.cpp:759
void set_escape_disabled(const bool escape_disabled)
Disable the escape key.
Definition: window.hpp:299
#define N_(String)
Definition: gettext.hpp:101
This file contains the window object, this object is a top level container which has the event manage...
static lg::log_domain log_loadscreen("loadscreen")
#define WRN_LS
#define LOG_LS
static const std::map< loading_stage, std::string > stage_names
static lg::log_domain log_display("display")
#define DBG_DP
loading_stage
Loading screen stage IDs.
Standard logging facilities (interface).
#define REGISTER_DIALOG(window_id)
Wrapper for REGISTER_DIALOG2.
@ WAIT
Definition: cursor.hpp:29
int get_frame_length()
Returns the length of one display frame, in milliseconds.
void raise_drawable(top_level_drawable *tld)
Raise a TLD to the top of the drawing stack.
bool is_in_main_thread()
Definition: events.cpp:473
void pump_and_draw()
pump() then immediately draw()
Definition: events.hpp:150
bool headless()
The game is running headless.
Definition: video.cpp:142
Contains the SDL_Rect helper code.
This file contains the settings handling of the widget library.
Contains the gui2 timer routines.
#define f