OpenTTD Source  20241120-master-g6d3adc6169
bootstrap_gui.cpp
Go to the documentation of this file.
1 /*
2  * This file is part of OpenTTD.
3  * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
4  * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
5  * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
6  */
7 
10 #include "stdafx.h"
11 #include "base_media_base.h"
12 #include "blitter/factory.hpp"
13 #include "error_func.h"
14 
15 #if defined(WITH_FREETYPE) || defined(WITH_UNISCRIBE) || defined(WITH_COCOA)
16 
17 #include "core/geometry_func.hpp"
18 #include "error.h"
19 #include "fontcache.h"
20 #include "gfx_func.h"
21 #include "network/network.h"
23 #include "openttd.h"
24 #include "strings_func.h"
25 #include "video/video_driver.hpp"
26 #include "window_func.h"
27 
29 
30 #include "table/strings.h"
31 
32 #include "safeguards.h"
33 
35 static constexpr NWidgetPart _background_widgets[] = {
36  NWidget(WWT_PANEL, COLOUR_DARK_BLUE, WID_BB_BACKGROUND), SetResize(1, 1),
37  EndContainer(),
38 };
39 
44  WDP_MANUAL, nullptr, 0, 0,
48 );
49 
51 class BootstrapBackground : public Window {
52 public:
54  {
55  this->InitNested(0);
57  ResizeWindow(this, _screen.width, _screen.height);
58  }
59 
60  void DrawWidget(const Rect &r, WidgetID) const override
61  {
62  GfxFillRect(r.left, r.top, r.right, r.bottom, 4, FILLRECT_OPAQUE);
63  GfxFillRect(r.left, r.top, r.right, r.bottom, 0, FILLRECT_CHECKER);
64  }
65 };
66 
70  NWidget(WWT_CAPTION, COLOUR_GREY, WID_BEM_CAPTION), SetDataTip(STR_MISSING_GRAPHICS_ERROR_TITLE, STR_NULL),
71  EndContainer(),
74  NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_BEM_QUIT), SetDataTip(STR_MISSING_GRAPHICS_ERROR_QUIT, STR_NULL), SetFill(1, 0),
75  EndContainer(),
76 };
77 
80  WDP_CENTER, nullptr, 0, 0,
84 );
85 
87 class BootstrapErrorWindow : public Window {
88 public:
90  {
91  this->InitNested(1);
92  }
93 
94  void Close([[maybe_unused]] int data = 0) override
95  {
96  _exit_game = true;
97  this->Window::Close();
98  }
99 
100  void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override
101  {
102  if (widget == WID_BEM_MESSAGE) {
103  size = GetStringBoundingBox(STR_MISSING_GRAPHICS_ERROR);
106  }
107  }
108 
109  void DrawWidget(const Rect &r, WidgetID widget) const override
110  {
111  if (widget == WID_BEM_MESSAGE) {
112  DrawStringMultiLine(r.Shrink(WidgetDimensions::scaled.frametext), STR_MISSING_GRAPHICS_ERROR, TC_FROMSTRING, SA_CENTER);
113  }
114  }
115 
116  void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override
117  {
118  if (widget == WID_BEM_QUIT) {
119  _exit_game = true;
120  }
121  }
122 };
123 
126  NWidget(WWT_CAPTION, COLOUR_GREY), SetDataTip(STR_CONTENT_DOWNLOAD_TITLE, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
127  NWidget(WWT_PANEL, COLOUR_GREY),
129  NWidget(WWT_EMPTY, INVALID_COLOUR, WID_NCDS_PROGRESS_BAR), SetFill(1, 0),
130  NWidget(WWT_EMPTY, INVALID_COLOUR, WID_NCDS_PROGRESS_TEXT), SetFill(1, 0), SetMinimalSize(350, 0),
131  EndContainer(),
132  EndContainer(),
133 };
134 
137  WDP_CENTER, nullptr, 0, 0,
141 );
142 
143 
146 public:
149  {
150  }
151 
152  void Close([[maybe_unused]] int data = 0) override
153  {
154  /* If we are not set to exit the game, it means the bootstrap failed. */
155  if (!_exit_game) {
156  new BootstrapErrorWindow();
157  }
158  this->BaseNetworkContentDownloadStatusWindow::Close();
159  }
160 
161  void OnDownloadComplete(ContentID) override
162  {
163  /* We have completed downloading. We can trigger finding the right set now. */
165 
166  /* And continue going into the menu. */
167  _game_mode = GM_MENU;
168 
169  /* _exit_game is used to break out of the outer video driver's MainLoop. */
170  _exit_game = true;
171  this->Close();
172  }
173 };
174 
176 static constexpr NWidgetPart _bootstrap_query_widgets[] = {
178  NWidget(WWT_CAPTION, COLOUR_GREY), SetDataTip(STR_MISSING_GRAPHICS_SET_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
179  EndContainer(),
182  NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_BAFD_YES), SetDataTip(STR_MISSING_GRAPHICS_YES_DOWNLOAD, STR_NULL),
183  NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_BAFD_NO), SetDataTip(STR_MISSING_GRAPHICS_NO_QUIT, STR_NULL),
184  EndContainer(),
185 };
186 
189  WDP_CENTER, nullptr, 0, 0,
191  WDF_NO_CLOSE,
193 );
194 
198 
199 public:
202  {
205  }
206 
208  void Close([[maybe_unused]] int data = 0) override
209  {
211  this->Window::Close();
212  }
213 
214  void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override
215  {
216  /* We cache the button size. This is safe as no reinit can happen here. */
217  if (this->button_size.width == 0) {
218  this->button_size = maxdim(GetStringBoundingBox(STR_MISSING_GRAPHICS_YES_DOWNLOAD), GetStringBoundingBox(STR_MISSING_GRAPHICS_NO_QUIT));
219  this->button_size.width += WidgetDimensions::scaled.frametext.Horizontal();
220  this->button_size.height += WidgetDimensions::scaled.frametext.Vertical();
221  }
222 
223  switch (widget) {
224  case WID_BAFD_QUESTION:
225  /* The question is twice as wide as the buttons, and determine the height based on the width. */
226  size.width = this->button_size.width * 2;
227  size.height = GetStringHeight(STR_MISSING_GRAPHICS_SET_MESSAGE, size.width - WidgetDimensions::scaled.frametext.Horizontal()) + WidgetDimensions::scaled.frametext.Vertical();
228  break;
229 
230  case WID_BAFD_YES:
231  case WID_BAFD_NO:
232  size = this->button_size;
233  break;
234  }
235  }
236 
237  void DrawWidget(const Rect &r, WidgetID widget) const override
238  {
239  if (widget != WID_BAFD_QUESTION) return;
240 
241  DrawStringMultiLine(r.Shrink(WidgetDimensions::scaled.frametext), STR_MISSING_GRAPHICS_SET_MESSAGE, TC_FROMSTRING, SA_CENTER);
242  }
243 
244  void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override
245  {
246  switch (widget) {
247  case WID_BAFD_YES:
248  /* We got permission to connect! Yay! */
250  break;
251 
252  case WID_BAFD_NO:
253  _exit_game = true;
254  break;
255 
256  default:
257  break;
258  }
259  }
260 
261  void OnConnect(bool success) override
262  {
263  if (!success) {
264  UserError("Failed to connect to content server. Please acquire a graphics set for OpenTTD. See section 1.4 of README.md.");
265  /* _exit_game is used to break out of the outer video driver's MainLoop. */
266  _exit_game = true;
267  this->Close();
268  return;
269  }
270 
271  /* Once connected, request the metadata. */
273  }
274 
275  void OnReceiveContentInfo(const ContentInfo *ci) override
276  {
277  /* And once the meta data is received, start downloading it. */
280  this->Close();
281  }
282 };
283 
284 #endif /* defined(WITH_FREETYPE) */
285 
286 #if defined(__EMSCRIPTEN__)
287 # include <emscripten.h>
288 # include "network/network.h"
289 # include "network/network_content.h"
290 # include "openttd.h"
291 # include "video/video_driver.hpp"
292 
293 class BootstrapEmscripten : public ContentCallback {
294  bool downloading = false;
295  uint total_files = 0;
296  uint total_bytes = 0;
297  uint downloaded_bytes = 0;
298 
299 public:
300  BootstrapEmscripten()
301  {
304  }
305 
306  ~BootstrapEmscripten()
307  {
309  }
310 
311  void OnConnect(bool success) override
312  {
313  if (!success) {
314  EM_ASM({ if (window["openttd_bootstrap_failed"]) openttd_bootstrap_failed(); });
315  return;
316  }
317 
318  /* Once connected, request the metadata. */
320  }
321 
322  void OnReceiveContentInfo(const ContentInfo *ci) override
323  {
324  if (this->downloading) return;
325 
326  /* And once the metadata is received, start downloading it. */
328  _network_content_client.DownloadSelectedContent(this->total_files, this->total_bytes);
329  this->downloading = true;
330 
331  EM_ASM({ if (window["openttd_bootstrap"]) openttd_bootstrap($0, $1); }, this->downloaded_bytes, this->total_bytes);
332  }
333 
334  void OnDownloadProgress(const ContentInfo *, int bytes) override
335  {
336  /* A negative value means we are resetting; for example, when retrying or using a fallback. */
337  if (bytes < 0) {
338  this->downloaded_bytes = 0;
339  } else {
340  this->downloaded_bytes += bytes;
341  }
342 
343  EM_ASM({ if (window["openttd_bootstrap"]) openttd_bootstrap($0, $1); }, this->downloaded_bytes, this->total_bytes);
344  }
345 
346  void OnDownloadComplete(ContentID) override
347  {
348  /* _exit_game is used to break out of the outer video driver's MainLoop. */
349  _exit_game = true;
350 
351  delete this;
352  }
353 };
354 #endif /* __EMSCRIPTEN__ */
355 
363 {
364  if (BaseGraphics::GetUsedSet() != nullptr) return true;
365 
366  /* No user interface, bail out with an error. */
367  if (BlitterFactory::GetCurrentBlitter()->GetScreenDepth() == 0) goto failure;
368 
369  /* If there is no network or no non-sprite font, then there is nothing we can do. Go straight to failure. */
370 #if defined(__EMSCRIPTEN__) || (defined(_WIN32) && defined(WITH_UNISCRIBE)) || (defined(WITH_FREETYPE) && (defined(WITH_FONTCONFIG) || defined(__APPLE__))) || defined(WITH_COCOA)
371  if (!_network_available) goto failure;
372 
373  /* First tell the game we're bootstrapping. */
374  _game_mode = GM_BOOTSTRAP;
375 
376 #if defined(__EMSCRIPTEN__)
377  new BootstrapEmscripten();
378 #else
379  /* Initialise the font cache. */
381  /* Next "force" finding a suitable non-sprite font as the local font is missing. */
382  CheckForMissingGlyphs(false);
383 
384  /* Initialise the palette. The biggest step is 'faking' some recolour sprites.
385  * This way the mauve and gray colours work and we can show the user interface. */
386  GfxInitPalettes();
387  static const int offsets[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x80, 0, 0, 0, 0x04, 0x08 };
388  for (Colours i = COLOUR_BEGIN; i != COLOUR_END; i++) {
389  for (ColourShade j = SHADE_BEGIN; j < SHADE_END; j++) {
390  SetColourGradient(i, j, offsets[i] + j);
391  }
392  }
393 
394  /* Finally ask the question. */
395  new BootstrapBackground();
397 #endif /* __EMSCRIPTEN__ */
398 
399  /* Process the user events. */
401 
402  /* _exit_game is used to get out of the video driver's main loop.
403  * In case GM_BOOTSTRAP is still set we did not exit it via the
404  * "download complete" event, so it was a manual exit. Obey it. */
405  _exit_game = _game_mode == GM_BOOTSTRAP;
406  if (_exit_game) return false;
407 
408  /* Try to probe the graphics. Should work this time. */
409  if (!BaseGraphics::SetSet({})) goto failure;
410 
411  /* Finally we can continue heading for the menu. */
412  _game_mode = GM_MENU;
413  return true;
414 #endif
415 
416  /* Failure to get enough working to get a graphics set. */
417 failure:
418  UserError("Failed to find a graphics set. Please acquire a graphics set for OpenTTD. See section 1.4 of README.md.");
419  return false;
420 }
Generic functions for replacing base data (graphics, sounds).
#define CLRBITS(x, y)
Clears several bits in a variable.
static constexpr NWidgetPart _background_widgets[]
Widgets for the background window to prevent smearing.
static WindowDesc _bootstrap_download_status_window_desc(WDP_CENTER, nullptr, 0, 0, WC_NETWORK_STATUS_WINDOW, WC_NONE, WDF_MODAL|WDF_NO_CLOSE, _nested_bootstrap_download_status_window_widgets)
Window description for the download window.
static constexpr NWidgetPart _bootstrap_query_widgets[]
The widgets for the query.
static constexpr NWidgetPart _nested_bootstrap_errmsg_widgets[]
Nested widgets for the error window.
static WindowDesc _bootstrap_errmsg_desc(WDP_CENTER, nullptr, 0, 0, WC_BOOTSTRAP, WC_NONE, WDF_MODAL|WDF_NO_CLOSE, _nested_bootstrap_errmsg_widgets)
Window description for the error window.
static WindowDesc _background_desc(WDP_MANUAL, nullptr, 0, 0, WC_BOOTSTRAP, WC_NONE, WDF_NO_CLOSE, _background_widgets)
Window description for the background window to prevent smearing.
bool HandleBootstrap()
Handle all procedures for bootstrapping OpenTTD without a base graphics set.
static WindowDesc _bootstrap_query_desc(WDP_CENTER, nullptr, 0, 0, WC_CONFIRM_POPUP_QUERY, WC_NONE, WDF_NO_CLOSE, _bootstrap_query_widgets)
The window description for the query.
static constexpr NWidgetPart _nested_bootstrap_download_status_window_widgets[]
Nested widgets for the download window.
Types related to the bootstrap widgets.
@ WID_BAFD_QUESTION
The question whether to download.
@ WID_BAFD_NO
An negative answer to the question.
@ WID_BAFD_YES
An affirmative answer to the question.
@ WID_BB_BACKGROUND
Background of the window.
@ WID_BEM_MESSAGE
Error message.
@ WID_BEM_QUIT
Quit button.
@ WID_BEM_CAPTION
Caption of the window.
static const GraphicsSet * GetUsedSet()
Return the used set.
static uint FindSets()
Do the scan for files.
static bool SetSet(const GraphicsSet *set)
Set the set to be used.
Base window for showing the download status of content.
static Blitter * GetCurrentBlitter()
Get the current active blitter (always set by calling SelectBlitter).
Definition: factory.hpp:138
The window for the query.
Dimension button_size
The dimension of the button.
void Close([[maybe_unused]] int data=0) override
Stop listening to the content client events.
BootstrapAskForDownloadWindow()
Start listening to the content client events.
The background for the game.
The window for a failed bootstrap.
void DownloadSelectedContent(uint &files, uint &bytes, bool fallback=false)
Actually begin downloading the content we selected.
void RemoveCallback(ContentCallback *cb)
Remove a callback.
void Select(ContentID cid)
Select a specific content id.
void RequestContentList(ContentType type)
Request the content list for the given type.
void AddCallback(ContentCallback *cb)
Add a callback to this class.
void Connect()
Connect with the content server.
virtual void MainLoop()=0
Perform the actual drawing.
static VideoDriver * GetInstance()
Get the currently active instance of the video driver.
RectPadding frametext
Padding inside frame with text.
Definition: window_gui.h:43
static WidgetDimensions scaled
Widget dimensions scaled for current zoom level.
Definition: window_gui.h:68
static const WidgetDimensions unscaled
Unscaled widget dimensions.
Definition: window_gui.h:67
Functions related to errors.
Error reporting related functions.
Factory to 'query' all available blitters.
Functions to read fonts from files and cache them.
void InitializeUnicodeGlyphMap()
Initialize the glyph map.
Definition: fontcache.h:158
Dimension maxdim(const Dimension &d1, const Dimension &d2)
Compute bounding box of both dimensions.
Geometry functions.
int GetStringHeight(std::string_view str, int maxw, FontSize fontsize)
Calculates height of string (in pixels).
Definition: gfx.cpp:704
Dimension GetStringBoundingBox(std::string_view str, FontSize start_fontsize)
Return the string dimension in pixels.
Definition: gfx.cpp:851
void GfxFillRect(int left, int top, int right, int bottom, int colour, FillRectMode mode)
Applies a certain FillRectMode-operation to a rectangle [left, right] x [top, bottom] on the screen.
Definition: gfx.cpp:114
int DrawStringMultiLine(int left, int right, int top, int bottom, std::string_view str, TextColour colour, StringAlignment align, bool underline, FontSize fontsize)
Draw string, possibly over multiple lines.
Definition: gfx.cpp:774
Functions related to the gfx engine.
@ SA_CENTER
Center both horizontally and vertically.
Definition: gfx_type.h:353
@ FILLRECT_CHECKER
Draw only every second pixel, used for greying-out.
Definition: gfx_type.h:299
@ FILLRECT_OPAQUE
Fill rectangle with a single colour.
Definition: gfx_type.h:298
constexpr NWidgetPart SetFill(uint16_t fill_x, uint16_t fill_y)
Widget part function for setting filling.
Definition: widget_type.h:1181
constexpr NWidgetPart SetPIP(uint8_t pre, uint8_t inter, uint8_t post)
Widget part function for setting a pre/inter/post spaces.
Definition: widget_type.h:1260
constexpr NWidgetPart SetPadding(uint8_t top, uint8_t right, uint8_t bottom, uint8_t left)
Widget part function for setting additional space around a widget.
Definition: widget_type.h:1228
constexpr NWidgetPart SetDataTip(uint32_t data, StringID tip)
Widget part function for setting the data and tooltip.
Definition: widget_type.h:1202
constexpr NWidgetPart SetMinimalSize(int16_t x, int16_t y)
Widget part function for setting the minimal size.
Definition: widget_type.h:1137
constexpr NWidgetPart NWidget(WidgetType tp, Colours col, WidgetID idx=-1)
Widget part function for starting a new 'real' widget.
Definition: widget_type.h:1309
constexpr NWidgetPart EndContainer()
Widget part function for denoting the end of a container (horizontal, vertical, WWT_FRAME,...
Definition: widget_type.h:1191
constexpr NWidgetPart SetResize(int16_t dx, int16_t dy)
Widget part function for setting the resize step.
Definition: widget_type.h:1126
bool _network_available
is network mode available?
Definition: network.cpp:67
Basic functions/variables used all over the place.
ClientNetworkContentSocketHandler _network_content_client
The client we use to connect to the server.
Part of the network protocol handling content distribution.
User interface for downloading files.
@ WID_NCDS_PROGRESS_TEXT
Text explaining what is happening.
@ WID_NCDS_PROGRESS_BAR
Simple progress bar.
Some generic types.
void SetColourGradient(Colours colour, ColourShade shade, uint8_t palette_index)
Set colour gradient palette index.
Definition: palette.cpp:325
A number of safeguards to prevent using unsafe methods.
Definition of base types and functions in a cross-platform compatible way.
void CheckForMissingGlyphs(bool base_font, MissingGlyphSearcher *searcher)
Check whether the currently loaded language pack uses characters that the currently loaded font does ...
Definition: strings.cpp:2265
Functions related to OTTD's strings.
Window for showing the download status of content.
BootstrapContentDownloadStatusWindow()
Simple call the constructor of the superclass.
Callbacks for notifying others about incoming data.
virtual void OnConnect([[maybe_unused]] bool success)
Callback for when the connection has finished.
virtual void OnDownloadProgress([[maybe_unused]] const ContentInfo *ci, [[maybe_unused]] int bytes)
We have progress in the download of a file.
virtual void OnDownloadComplete([[maybe_unused]] ContentID cid)
We have finished downloading a file.
virtual void OnReceiveContentInfo([[maybe_unused]] const ContentInfo *ci)
We received a content info.
Container for all important information about a piece of content.
ContentID id
Unique (server side) ID for the content.
Dimensions (a width and height) of a rectangle in 2D.
Partial widget specification to allow NWidgets to be written nested.
Definition: widget_type.h:1075
Coordinates of a point in 2D.
constexpr uint Horizontal() const
Get total horizontal padding of RectPadding.
constexpr uint Vertical() const
Get total vertical padding of RectPadding.
Specification of a rectangle with absolute coordinates of all edges.
Rect Shrink(int s) const
Copy and shrink Rect by s pixels.
High level window description.
Definition: window_gui.h:159
Data structure for an opened window.
Definition: window_gui.h:273
virtual void Close(int data=0)
Hide the window and all its child windows, and mark them for a later deletion.
Definition: window.cpp:1047
ResizeInfo resize
Resize information.
Definition: window_gui.h:314
Window(WindowDesc &desc)
Empty constructor, initialization has been moved to InitNested() called from the constructor of the d...
Definition: window.cpp:1756
void InitNested(WindowNumber number=0)
Perform complete initialization of the Window with nested widgets, to allow use.
Definition: window.cpp:1746
WindowFlags flags
Window flags.
Definition: window_gui.h:300
@ CONTENT_TYPE_BASE_GRAPHICS
The content consists of base graphics.
ContentID
Unique identifier for the content.
Base of all video drivers.
@ WWT_PUSHTXTBTN
Normal push-button (no toggle button) with text caption.
Definition: widget_type.h:112
@ NWID_HORIZONTAL
Horizontal container.
Definition: widget_type.h:75
@ WWT_PANEL
Simple depressed panel.
Definition: widget_type.h:50
@ WWT_CAPTION
Window caption (window title between closebox and stickybox)
Definition: widget_type.h:61
@ NWID_VERTICAL
Vertical container.
Definition: widget_type.h:77
@ WWT_EMPTY
Empty widget, place holder to reserve space in widget tree.
Definition: widget_type.h:48
void ResizeWindow(Window *w, int delta_x, int delta_y, bool clamp_to_screen, bool schedule_resize)
Resize the window.
Definition: window.cpp:2022
Window functions not directly related to making/drawing windows.
@ WF_WHITE_BORDER
Window white border counter bit mask.
Definition: window_gui.h:236
@ WDF_NO_CLOSE
This window can't be interactively closed.
Definition: window_gui.h:206
@ WDF_MODAL
The window is a modal child of some other window, meaning the parent is 'inactive'.
Definition: window_gui.h:204
@ WDP_CENTER
Center the window.
Definition: window_gui.h:148
@ WDP_MANUAL
Manually align the window (so no automatic location finding)
Definition: window_gui.h:146
int WidgetID
Widget ID.
Definition: window_type.h:18
@ WN_CONFIRM_POPUP_QUERY_BOOTSTRAP
Query popup confirm for bootstrap.
Definition: window_type.h:33
@ WC_NONE
No window, redirects to WC_MAIN_WINDOW.
Definition: window_type.h:45
@ WC_CONFIRM_POPUP_QUERY
Popup with confirm question; Window numbers:
Definition: window_type.h:130
@ WC_BOOTSTRAP
Bootstrap; Window numbers:
Definition: window_type.h:655
@ WC_NETWORK_STATUS_WINDOW
Network status window; Window numbers:
Definition: window_type.h:491