The Battle for Wesnoth  1.19.0-dev
build_info.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2015 - 2024
3  by Iris Morelle <shadowm2006@gmail.com>
4  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
5 
6  This program is free software; you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation; either version 2 of the License, or
9  (at your option) any later version.
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY.
12 
13  See the COPYING file for more details.
14 */
15 
16 #define GETTEXT_DOMAIN "wesnoth-lib"
17 
18 #include "build_info.hpp"
19 
20 #include "desktop/version.hpp"
21 #include "filesystem.hpp"
22 #include "formatter.hpp"
23 #include "gettext.hpp"
26 #include "game_version.hpp"
27 #include "sound.hpp"
28 #include "video.hpp"
29 #include "addon/manager.hpp"
30 #include "sdl/point.hpp"
31 
32 #include <algorithm>
33 #include <fstream>
34 #include <iomanip>
35 
36 #include "lua/wrapper_lua.h"
37 
38 #include <SDL2/SDL_image.h>
39 #include <SDL2/SDL_mixer.h>
40 
41 #include <boost/algorithm/string.hpp>
42 #include <boost/version.hpp>
43 
44 #ifndef __APPLE__
45 #include <openssl/crypto.h>
46 #include <openssl/opensslv.h>
47 #endif
48 
49 #include <curl/curl.h>
50 
51 #include <pango/pangocairo.h>
52 
53 #ifdef __APPLE__
54 // apple_notification.mm uses Foundation.h, which is an Objective-C header;
55 // but CoreFoundation.h is a C header which also defines these.
56 #include <CoreFoundation/CoreFoundation.h>
57 #endif
58 
59 namespace game_config
60 {
61 
62 namespace {
63 
64 struct version_table_manager
65 {
66  std::vector<std::string> compiled, linked, names;
67  std::vector<optional_feature> features;
68 
69  version_table_manager();
70 };
71 
72 const version_table_manager versions;
73 
74 std::string format_version(unsigned a, unsigned b, unsigned c)
75 {
76  return formatter() << a << '.' << b << '.' << c;
77 }
78 
79 std::string format_version(const SDL_version& v)
80 {
81  return formatter() << static_cast<unsigned>(v.major) << '.'
82  << static_cast<unsigned>(v.minor) << '.'
83  << static_cast<unsigned>(v.patch);
84 }
85 
86 #ifndef __APPLE__
87 
88 std::string format_openssl_patch_level(uint8_t p)
89 {
90  return p <= 26
91  ? std::string(1, 'a' + static_cast<char>(p) - 1)
92  : "patch" + std::to_string(p);
93 }
94 
95 std::string format_openssl_version(long v)
96 {
97  int major, minor, fix, patch, status;
98  std::ostringstream fmt;
99 
100  //
101  // The people who maintain OpenSSL are not from this world. I suppose it's
102  // only fair that I'm the one who gets to try to make sense of their version
103  // encoding scheme. -- shadowm
104  //
105 
106  if(v < 0x0930L) {
107  // Pre-0.9.3 seems simpler times overall.
108  minor = v & 0x0F00L >> 8;
109  fix = v & 0x00F0L >> 4;
110  patch = v & 0x000FL;
111 
112  fmt << "0." << minor << '.' << fix;
113  if(patch) {
114  fmt << format_openssl_patch_level(patch);
115  }
116  } else {
117  //
118  // Note that they either assume the major version will never be greater than
119  // 9, they plan to use hexadecimal digits for versions 10.x.x through
120  // 15.x.x, or they expect long to be always > 32-bits by then. Who the hell
121  // knows, really.
122  //
123  major = (v & 0xF0000000L) >> 28;
124  minor = (v & 0x0FF00000L) >> 20;
125  fix = (v & 0x000FF000L) >> 12;
126  patch = (v & 0x00000FF0L) >> 4;
127  status = (v & 0x0000000FL);
128 
129  if(v < 0x00905100L) {
130  //
131  // From wiki.openssl.org (also mentioned in opensslv.h, in the most oblique
132  // fashion possible):
133  //
134  // "Versions between 0.9.3 and 0.9.5 had a version identifier with this interpretation:
135  // MMNNFFRBB major minor fix final beta/patch"
136  //
137  // Both the wiki and opensslv.h fail to accurately list actual version
138  // numbers that ended up used in the wild -- e.g. 0.9.3a is supposedly
139  // 0x0090301f when it really was 0x00903101.
140  //
141  const uint8_t is_final = (v & 0xF00L) >> 8;
142  status = is_final ? 0xF : 0;
143  patch = v & 0xFFL;
144  } else if(v < 0x00906000L) {
145  //
146  // Quoth opensslv.h:
147  //
148  // "For continuity reasons (because 0.9.5 is already out, and is coded
149  // 0x00905100), between 0.9.5 and 0.9.6 the coding of the patch level
150  // part is slightly different, by setting the highest bit. This means
151  // that 0.9.5a looks like this: 0x0090581f. At 0.9.6, we can start
152  // with 0x0090600S..."
153  //
154  patch ^= 1 << 7;
155  }
156 
157  fmt << major << '.' << minor << '.' << fix;
158 
159  if(patch) {
160  fmt << format_openssl_patch_level(patch);
161  }
162 
163  if(status == 0x0) {
164  fmt << "-dev";
165  } else if(status < 0xF) {
166  fmt << "-beta" << status;
167  }
168  }
169 
170  return fmt.str();
171 }
172 
173 #endif
174 
175 version_table_manager::version_table_manager()
176  : compiled(LIB_COUNT, "")
177  , linked(LIB_COUNT, "")
178  , names(LIB_COUNT, "")
179  , features()
180 {
181  SDL_version sdl_version;
182 
183  //
184  // SDL
185  //
186 
187  SDL_VERSION(&sdl_version);
188  compiled[LIB_SDL] = format_version(sdl_version);
189 
190  SDL_GetVersion(&sdl_version);
191  linked[LIB_SDL] = format_version(sdl_version);
192 
193  names[LIB_SDL] = "SDL";
194 
195  //
196  // SDL_image
197  //
198 
199  SDL_IMAGE_VERSION(&sdl_version);
200  compiled[LIB_SDL_IMAGE] = format_version(sdl_version);
201 
202  const SDL_version* sdl_rt_version = IMG_Linked_Version();
203  if(sdl_rt_version) {
204  linked[LIB_SDL_IMAGE] = format_version(*sdl_rt_version);
205  }
206 
207  names[LIB_SDL_IMAGE] = "SDL_image";
208 
209  //
210  // SDL_mixer
211  //
212 
213  SDL_MIXER_VERSION(&sdl_version);
214  compiled[LIB_SDL_MIXER] = format_version(sdl_version);
215 
216  sdl_rt_version = Mix_Linked_Version();
217  if(sdl_rt_version) {
218  linked[LIB_SDL_MIXER] = format_version(*sdl_rt_version);
219  }
220 
221  names[LIB_SDL_MIXER] = "SDL_mixer";
222 
223  //
224  // Boost
225  //
226 
227  compiled[LIB_BOOST] = BOOST_LIB_VERSION;
228  std::replace(compiled[LIB_BOOST].begin(), compiled[LIB_BOOST].end(), '_', '.');
229  names[LIB_BOOST] = "Boost";
230 
231  //
232  // Lua
233  //
234 
235  compiled[LIB_LUA] = LUA_VERSION_MAJOR "." LUA_VERSION_MINOR "." LUA_VERSION_RELEASE;
236  names[LIB_LUA] = "Lua";
237 
238  //
239  // OpenSSL/libcrypto
240  //
241 
242 #ifndef __APPLE__
243  compiled[LIB_CRYPTO] = format_openssl_version(OPENSSL_VERSION_NUMBER);
244  linked[LIB_CRYPTO] = format_openssl_version(SSLeay());
245  names[LIB_CRYPTO] = "OpenSSL/libcrypto";
246 #endif
247 
248  //
249  // libcurl
250  //
251 
252  compiled[LIB_CURL] = format_version(
253  (LIBCURL_VERSION_NUM & 0xFF0000) >> 16,
254  (LIBCURL_VERSION_NUM & 0x00FF00) >> 8,
255  LIBCURL_VERSION_NUM & 0x0000FF);
256  curl_version_info_data *curl_ver = curl_version_info(CURLVERSION_NOW);
257  if(curl_ver && curl_ver->version) {
258  linked[LIB_CURL] = curl_ver->version;
259  }
260  // This is likely to upset somebody out there, but the cURL authors
261  // consistently call it 'libcurl' (all lowercase) in all documentation.
262  names[LIB_CURL] = "libcurl";
263 
264  //
265  // Cairo
266  //
267 
268  compiled[LIB_CAIRO] = CAIRO_VERSION_STRING;
269  linked[LIB_CAIRO] = cairo_version_string();
270  names[LIB_CAIRO] = "Cairo";
271 
272  //
273  // Pango
274  //
275 
276  compiled[LIB_PANGO] = PANGO_VERSION_STRING;
277  linked[LIB_PANGO] = pango_version_string();
278  names[LIB_PANGO] = "Pango";
279 
280  //
281  // Features table.
282  //
283 
284  features.emplace_back(N_("feature^Lua console completion"));
285 #ifdef HAVE_HISTORY
286  features.back().enabled = true;
287 #endif
288 
289 #ifdef _X11
290 
291  features.emplace_back(N_("feature^D-Bus notifications back end"));
292 #ifdef HAVE_LIBDBUS
293  features.back().enabled = true;
294 #endif
295 
296 #endif /* _X11 */
297 
298 #ifdef _WIN32
299  // Always compiled in.
300  features.emplace_back(N_("feature^Win32 notifications back end"));
301  features.back().enabled = true;
302 #endif
303 
304 #ifdef __APPLE__
305  // Always compiled in.
306  features.emplace_back(N_("feature^Cocoa notifications back end"));
307  features.back().enabled = true;
308 #endif /* __APPLE__ */
309 }
310 
311 const std::string empty_version = "";
312 
313 } // end anonymous namespace 1
314 
315 std::string build_arch()
316 {
317 #if BOOST_ARCH_X86_64
318  return "x86_64";
319 #elif BOOST_ARCH_X86_32
320  return "x86";
321 #elif BOOST_ARCH_ARM && (defined(__arm64) || defined(_M_ARM64))
322  return "arm64";
323 #elif BOOST_ARCH_ARM
324  return "arm";
325 #elif BOOST_ARCH_IA64
326  return "ia64";
327 #elif BOOST_ARCH_PPC
328  return "ppc";
329 #elif BOOST_ARCH_ALPHA
330  return "alpha";
331 #elif BOOST_ARCH_MIPS
332  return "mips";
333 #elif BOOST_ARCH_SPARC
334  return "sparc";
335 #else
336  // Congratulations, you're running Wesnoth on an exotic platform -- either that or you live in
337  // the foretold future where x86 and ARM stopped being the dominant CPU architectures for the
338  // general-purpose consumer market. If you want to add label support for your platform, check
339  // out the Boost.Predef library's documentation and alter the code above accordingly.
340  //
341  // On the other hand, if you got here looking for Wesnoth's biggest secret let me just say
342  // right here and now that Irdya is round. There, I said the thing that nobody has dared say
343  // in mainline content before.
344  return _("cpu_architecture^<unknown>");
345 #endif
346 }
347 
348 std::vector<optional_feature> optional_features_table(bool localize)
349 {
350  std::vector<optional_feature> res = versions.features;
351 
352  for(std::size_t k = 0; k < res.size(); ++k) {
353  if(localize) {
354  res[k].name = _(res[k].name.c_str());
355  } else {
356  // Strip annotation carets ("blah blah^actual text here") from translatable
357  // strings.
358  const auto caret_pos = res[k].name.find('^');
359  if(caret_pos != std::string::npos) {
360  res[k].name.erase(0, caret_pos + 1);
361  }
362  }
363  }
364  return res;
365 }
366 
367 const std::string& library_build_version(LIBRARY_ID lib)
368 {
369  if(lib >= LIB_COUNT) {
370  return empty_version;
371  }
372 
373  return versions.compiled[lib];
374 }
375 
376 const std::string& library_runtime_version(LIBRARY_ID lib)
377 {
378  if(lib >= LIB_COUNT) {
379  return empty_version;
380  }
381 
382  return versions.linked[lib];
383 }
384 
385 const std::string& library_name(LIBRARY_ID lib)
386 {
387  if(lib >= LIB_COUNT) {
388  return empty_version;
389  }
390 
391  return versions.names[lib];
392 }
393 
394 std::string dist_channel_id()
395 {
396  std::string info;
397  std::ifstream infofile(game_config::path + "/data/dist");
398  if(infofile.is_open()) {
399  std::getline(infofile, info);
400  infofile.close();
401  boost::trim(info);
402  }
403 
404  if(info.empty()) {
405  return "Default";
406  }
407 
408  return info;
409 }
410 
411 namespace {
412 
413 /**
414  * Formats items into a tidy 2-column list with a fixed-length first column.
415  */
416 class list_formatter
417 {
418 public:
419  using list_entry = std::pair<std::string, std::string>;
420  using contents_list = std::vector<list_entry>;
421 
422  list_formatter(const std::string& heading, const contents_list& contents = {}, const std::string& empty_placeholder = "")
423  : heading_(heading)
424  , placeholder_(empty_placeholder)
425  , contents_(contents)
426  {
427  }
428 
429  void insert(const std::string& label, const std::string& value)
430  {
431  contents_.emplace_back(label, value);
432  }
433 
434  void set_placeholder(const std::string& placeholder)
435  {
436  placeholder_ = placeholder;
437  }
438 
439  void stream_put(std::ostream& os) const;
440 
441 private:
442  static const char heading_delimiter;
443  static const std::string label_delimiter;
444 
445  std::string heading_;
446  std::string placeholder_;
447 
448  contents_list contents_;
449 };
450 
451 const char list_formatter::heading_delimiter = '=';
452 const std::string list_formatter::label_delimiter = ": ";
453 
454 void list_formatter::stream_put(std::ostream& os) const
455 {
456  if(!heading_.empty()) {
457  os << heading_ << '\n' << std::string(utf8::size(heading_), heading_delimiter) << "\n\n";
458  }
459 
460  if(contents_.empty() && !placeholder_.empty()) {
461  os << placeholder_ << '\n';
462  } else if(!contents_.empty()) {
463  auto label_length_comparator = [](const list_entry& a, const list_entry& b)
464  {
465  return utf8::size(a.first) < utf8::size(b.first);
466  };
467 
468  const auto longest_entry_label = std::max_element(contents_.begin(), contents_.end(), label_length_comparator);
469  const std::size_t min_length = longest_entry_label != contents_.end()
470  ? utf8::size(label_delimiter) + utf8::size(longest_entry_label->first)
471  : 0;
472 
473  // Save stream attributes for resetting them later after completing the loop
474  const std::size_t prev_width = os.width();
475  const std::ostream::fmtflags prev_flags = os.flags();
476 
477  os << std::left;
478 
479  for(const auto& entry : contents_) {
480  os << std::setw(min_length) << entry.first + label_delimiter << entry.second << '\n';
481  }
482 
483  os.width(prev_width);
484  os.flags(prev_flags);
485  }
486 
487  os << '\n';
488 }
489 
490 std::ostream& operator<<(std::ostream& os, const list_formatter& fmt)
491 {
492  fmt.stream_put(os);
493  return os;
494 }
495 
496 list_formatter library_versions_report_internal(const std::string& heading = "")
497 {
498  list_formatter fmt{heading};
499 
500  for(unsigned n = 0; n < LIB_COUNT; ++n)
501  {
502  if(versions.names[n].empty()) {
503  continue;
504  }
505 
506  std::string text = versions.compiled[n];
507  if(!versions.linked[n].empty()) {
508  text += " (runtime " + versions.linked[n] + ")";
509  }
510 
511  fmt.insert(versions.names[n], text);
512  }
513 
514  return fmt;
515 }
516 
517 list_formatter optional_features_report_internal(const std::string& heading = "")
518 {
519  list_formatter fmt{heading};
520 
521  const std::vector<optional_feature>& features = optional_features_table(false);
522 
523  for(const auto& feature : features) {
524  fmt.insert(feature.name, feature.enabled ? "yes" : "no");
525  }
526 
527  return fmt;
528 }
529 
530 inline std::string geometry_to_string(point p)
531 {
532  return std::to_string(p.x) + 'x' + std::to_string(p.y);
533 }
534 
535 template<typename coordinateType>
536 inline std::string geometry_to_string(coordinateType horizontal, coordinateType vertical)
537 {
538  // Use a stream in order to control significant digits in non-integers
539  return formatter() << std::fixed << std::setprecision(2) << horizontal << 'x' << vertical;
540 }
541 
542 std::string format_sdl_driver_list(std::vector<std::string> drivers, const std::string& current_driver)
543 {
544  bool found_current_driver = false;
545 
546  for(auto& drvname : drivers) {
547  if(current_driver == drvname) {
548  found_current_driver = true;
549  drvname = "[" + current_driver + "]";
550  }
551  }
552 
553  if(drivers.empty() || !found_current_driver) {
554  // This shouldn't happen but SDL is weird at times so whatevs
555  drivers.emplace_back("[" + current_driver + "]");
556  }
557 
558  return utils::join(drivers, " ");
559 }
560 
561 list_formatter video_settings_report_internal(const std::string& heading = "")
562 {
563  list_formatter fmt{heading};
564 
565  std::string placeholder;
566 
567  if(video::headless()) {
568  placeholder = "Running in non-interactive mode.";
569  }
570 
571  if(!video::has_window()) {
572  placeholder = "Video not initialized yet.";
573  }
574 
575  if(!placeholder.empty()) {
576  fmt.set_placeholder(placeholder);
577  return fmt;
578  }
579 
580  const auto& current_driver = video::current_driver();
581  auto drivers = video::enumerate_drivers();
582 
583  const auto& dpi = video::get_dpi();
584  std::string dpi_report;
585 
586  dpi_report = dpi.first == 0.0f || dpi.second == 0.0f ?
587  "<unknown>" :
588  geometry_to_string(dpi.first, dpi.second);
589 
590  fmt.insert("SDL video drivers", format_sdl_driver_list(drivers, current_driver));
591  fmt.insert("Window size", geometry_to_string(video::current_resolution()));
592  fmt.insert("Game canvas size", geometry_to_string(video::game_canvas_size()));
593  fmt.insert("Final render target size", geometry_to_string(video::output_size()));
594  fmt.insert("Screen refresh rate", std::to_string(video::current_refresh_rate()));
595  fmt.insert("Screen dpi", dpi_report);
596 
597  const auto& renderer_report = video::renderer_report();
598 
599  for(const auto& info : renderer_report) {
600  fmt.insert(info.first, info.second);
601  }
602 
603  return fmt;
604 }
605 
606 list_formatter sound_settings_report_internal(const std::string& heading = "")
607 {
608  list_formatter fmt{heading};
609 
610  const auto& driver_status = sound::driver_status::query();
611 
612  if(!driver_status.initialized) {
613  fmt.set_placeholder("Audio not initialized.");
614  return fmt;
615  }
616 
617  const auto& current_driver = sound::current_driver();
618  auto drivers = sound::enumerate_drivers();
619 
620  static std::map<uint16_t, std::string> audio_format_names = {
621  // 8 bits
622  { AUDIO_U8, "unsigned 8 bit" },
623  { AUDIO_S8, "signed 8 bit" },
624  // 16 bits
625  { AUDIO_U16LSB, "unsigned 16 bit little-endian" },
626  { AUDIO_U16MSB, "unsigned 16 bit big-endian" },
627  { AUDIO_S16LSB, "signed 16 bit little-endian" },
628  { AUDIO_S16MSB, "signed 16 bit big-endian" },
629  // 32 bits
630  { AUDIO_S32LSB, "signed 32 bit little-endian" },
631  { AUDIO_S32MSB, "signed 32 bit big-endian" },
632  { AUDIO_F32LSB, "signed 32 bit floating point little-endian" },
633  { AUDIO_F32MSB, "signed 32 bit floating point big-endian" },
634  };
635 
636  auto fmt_names_it = audio_format_names.find(driver_status.format);
637  // If we don't recognize the format id just print the raw number
638  const std::string fmt_name = fmt_names_it != audio_format_names.end()
639  ? fmt_names_it->second
640  : formatter() << "0x" << std::setfill('0') << std::setw(2*sizeof(driver_status.format)) << std::hex << std::uppercase << driver_status.format;
641 
642  fmt.insert("SDL audio drivers", format_sdl_driver_list(drivers, current_driver));
643  fmt.insert("Number of channels", std::to_string(driver_status.channels));
644  fmt.insert("Output rate", std::to_string(driver_status.frequency) + " Hz");
645  fmt.insert("Sample format", fmt_name);
646  fmt.insert("Sample size", std::to_string(driver_status.chunk_size) + " bytes");
647 
648  return fmt;
649 }
650 
651 } // end anonymous namespace 2
652 
654 {
655  return formatter{} << library_versions_report_internal();
656 }
657 
659 {
660  return formatter{} << optional_features_report_internal();
661 }
662 
663 std::string full_build_report()
664 {
665  list_formatter::contents_list paths{
666  {"Data dir", game_config::path},
667  {"User config dir", filesystem::get_user_config_dir()},
668  {"User data dir", filesystem::get_user_data_dir()},
669  {"Saves dir", filesystem::get_saves_dir()},
670  {"Add-ons dir", filesystem::get_addons_dir()},
671  {"Cache dir", filesystem::get_cache_dir()},
672  {"Logs dir", filesystem::get_logs_dir()},
673  };
674 
675  // Obfuscate usernames in paths
676  for(auto& entry : paths) {
677  entry.second = filesystem::sanitize_path(entry.second);
678  }
679 
680  list_formatter::contents_list addons;
681 
682  for(const auto& addon_info : installed_addons_and_versions()) {
683  addons.emplace_back(addon_info.first, addon_info.second);
684  }
685 
686  std::ostringstream o;
687 
688  o << "The Battle for Wesnoth version " << game_config::revision << " " << build_arch() << '\n'
689  << "Running on " << desktop::os_version() << '\n'
690  << "Distribution channel: " << dist_channel_id() << '\n'
691  << '\n'
692  << list_formatter{"Game paths", paths}
693  << library_versions_report_internal("Libraries")
694  << optional_features_report_internal("Features")
695  << video_settings_report_internal("Current video settings")
696  << sound_settings_report_internal("Current audio settings")
697  << list_formatter("Installed add-ons", addons, "No add-ons installed.");
698 
699  return o.str();
700 }
701 
702 } // end namespace game_config
std::ostream & operator<<(std::ostream &s, const ai::attack_result &r)
Definition: actions.cpp:1132
std::map< std::string, std::string > installed_addons_and_versions()
Retrieves the ids and versions of all installed add-ons.
Definition: manager.cpp:196
static const char heading_delimiter
Definition: build_info.cpp:442
std::vector< optional_feature > features
Definition: build_info.cpp:67
std::string heading_
Definition: build_info.cpp:445
std::vector< std::string > compiled
Definition: build_info.cpp:66
std::string placeholder_
Definition: build_info.cpp:446
contents_list contents_
Definition: build_info.cpp:448
std::vector< std::string > linked
Definition: build_info.cpp:66
std::vector< std::string > names
Definition: build_info.cpp:66
static const std::string label_delimiter
Definition: build_info.cpp:443
std::ostringstream wrapper.
Definition: formatter.hpp:40
Platform identification and version information functions.
Declarations for File-IO.
Interfaces for manipulating version numbers of engine, add-ons, etc.
#define N_(String)
Definition: gettext.hpp:101
static std::string _(const char *str)
Definition: gettext.hpp:93
std::string label
What to show in the filter's drop-down list.
Definition: manager.cpp:209
T end(const std::pair< T, T > &p)
T begin(const std::pair< T, T > &p)
std::string os_version()
Returns a string with the running OS name and version information.
Definition: version.cpp:216
std::string get_cache_dir()
Definition: filesystem.cpp:880
std::string get_user_config_dir()
Definition: filesystem.cpp:841
std::string get_user_data_dir()
Definition: filesystem.cpp:870
std::string get_saves_dir()
std::string get_logs_dir()
Definition: filesystem.cpp:875
std::string get_addons_dir()
std::string sanitize_path(const std::string &path)
Sanitizes a path to remove references to the user's name.
Game configuration data as global variables.
Definition: build_info.cpp:60
const std::string & library_name(LIBRARY_ID lib)
Retrieve the user-visible name for the given library.
Definition: build_info.cpp:385
std::string path
Definition: filesystem.cpp:83
std::string full_build_report()
Produce a bug report-style info dump.
Definition: build_info.cpp:663
std::string library_versions_report()
Produce a plain-text report of library versions suitable for stdout/stderr.
Definition: build_info.cpp:653
std::vector< optional_feature > optional_features_table(bool localize)
Retrieve the features table.
Definition: build_info.cpp:348
const std::string & library_runtime_version(LIBRARY_ID lib)
Retrieve the runtime version number of the given library.
Definition: build_info.cpp:376
const std::string & library_build_version(LIBRARY_ID lib)
Retrieve the build-time version number of the given library.
Definition: build_info.cpp:367
std::string build_arch()
Obtain the processor architecture for this build.
Definition: build_info.cpp:315
std::string optional_features_report()
Produce a plain-text report of features suitable for stdout/stderr.
Definition: build_info.cpp:658
const std::string revision
std::string dist_channel_id()
Return the distribution channel identifier, or "Default" if missing.
Definition: build_info.cpp:394
logger & info()
Definition: log.cpp:314
std::vector< std::string > enumerate_drivers()
Definition: sound.cpp:416
std::string current_driver()
Definition: sound.cpp:410
std::string & insert(std::string &str, const std::size_t pos, const std::string &insert)
Insert a UTF-8 string at the specified position.
Definition: unicode.cpp:98
std::size_t size(const std::string &str)
Length in characters of a UTF-8 string.
Definition: unicode.cpp:85
void trim(std::string_view &s)
std::string join(const T &v, const std::string &s=",")
Generates a new string joining container items in a list.
bool headless()
The game is running headless.
Definition: video.cpp:141
std::vector< std::pair< std::string, std::string > > renderer_report()
Provides diagnostic information about the current renderer for the build_info API.
Definition: video.cpp:863
point output_size()
Returns the size of the final render target.
Definition: video.cpp:412
point game_canvas_size()
The size of the game canvas, in drawing coordinates / game pixels.
Definition: video.cpp:434
bool has_window()
Whether the game has set up a window to render into.
Definition: video.cpp:407
std::vector< std::string > enumerate_drivers()
A list of available video drivers.
Definition: video.cpp:666
std::pair< float, float > get_dpi()
Retrieves the current game screen DPI for the build_info API.
Definition: video.cpp:843
point current_resolution()
The current window size in desktop coordinates.
Definition: video.cpp:756
int current_refresh_rate()
The refresh rate of the screen.
Definition: video.cpp:488
std::string current_driver()
The current video driver in use, or else "<not initialized>".
Definition: video.cpp:660
Holds a 2D point.
Definition: point.hpp:25
static driver_status query()
Definition: sound.cpp:429
mock_char c
mock_party p
static map_location::DIRECTION n
#define a
#define b