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