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