openMSX
main.cc
Go to the documentation of this file.
1 /*
2  * openmsx - the MSX emulator that aims for perfection
3  *
4  */
5 
6 #include "openmsx.hh"
7 #include "Date.hh"
8 #include "Reactor.hh"
9 #include "CommandLineParser.hh"
10 #include "CliServer.hh"
11 #include "Display.hh"
12 #include "EventDistributor.hh"
13 #include "RenderSettings.hh"
14 #include "EnumSetting.hh"
15 #include "MSXException.hh"
16 #include "Thread.hh"
17 #include "build-info.hh"
18 #include "random.hh"
19 #include <iostream>
20 #include <exception>
21 #include <ctime>
22 #include <cstdio>
23 #include <cstdlib>
24 #include <SDL.h>
25 #ifdef _WIN32
26 #include "win32-arggen.hh"
27 #endif
28 
29 // Set LOG_TO_FILE to 1 for any platform on which stdout and stderr must
30 // be redirected to a file
31 // Also, specify the appropriate file names, depending on the platform conventions
32 #if PLATFORM_ANDROID
33 #define LOG_TO_FILE 1
34 #define STDOUT_LOG_FILE_NAME "openmsx_system/openmsx.stdout"
35 #define STDERR_LOG_FILE_NAME "openmsx_system/openmsx.stderr"
36 #else
37 #define LOG_TO_FILE 0
38 #endif
39 
40 namespace openmsx {
41 
42 #ifdef _WIN32
43 // wrapper for Windows, as the MS runtime doesn't provide setenv!?
44 int setenv(const char *name, const char *value, int overwrite);
45 int setenv(const char *name, const char *value, int overwrite)
46 {
47  if (!overwrite) {
48  char* oldvalue = getenv(name);
49  if (oldvalue) {
50  return 0;
51  }
52  }
53  return _putenv_s(name, value);
54 }
55 #endif
56 
57 #ifdef _WIN32
58 // enable console output on Windows
59 void EnableConsoleOutput()
60 {
61  if (AttachConsole(ATTACH_PARENT_PROCESS))
62  {
63  FILE* pCout;
64  freopen_s(&pCout, "CONOUT$", "w", stdout);
65  std::cout.clear();
66  std::wcout.clear();
67  }
68 }
69 #endif
70 
71 static void initializeSDL()
72 {
73  int flags = 0;
74 #ifndef SDL_JOYSTICK_DISABLED
75  flags |= SDL_INIT_JOYSTICK;
76 #endif
77 #ifndef NDEBUG
78  flags |= SDL_INIT_NOPARACHUTE;
79 #endif
80  if (SDL_Init(flags) < 0) {
81  throw FatalError("Couldn't init SDL: ", SDL_GetError());
82  }
83 
84  // for now: instruct FreeType to use the v35 TTF engine
85  // this is the rendering we had before FreeType implemented and
86  // switched over to the v40 engine. To keep this the same for now, we
87  // select the old engine explicitly, until we decide how to continue
88  // (e.g. just use v40 or use a font that renders better on v40
89  setenv("FREETYPE_PROPERTIES", "truetype:interpreter-version=35", 0);
90 }
91 
92 static int main(int argc, char **argv)
93 {
94 #if LOG_TO_FILE
95  ad_printf("Redirecting stdout to %s and stderr to %s\n",
96  STDOUT_LOG_FILE_NAME, STDERR_LOG_FILE_NAME);
97 
98  if (!freopen(STDOUT_LOG_FILE_NAME, "a", stdout)) {
99  ad_printf("Couldn't redirect stdout to logfile, aborting\n");
100  std::cerr << "Couldn't redirect stdout to "
101  STDOUT_LOG_FILE_NAME "\n";
102  return 1;
103  }
104  if (!freopen(STDERR_LOG_FILE_NAME, "a", stderr)) {
105  ad_printf("Couldn't redirect stderr to logfile, aborting\n");
106  std::cout << "Couldn't redirect stderr to "
107  STDERR_LOG_FILE_NAME "\n";
108  return 1;
109  }
110 
111  std::string msg = Date::toString(time(nullptr)) + ": starting openMSX";
112  std::cout << msg << '\n';
113  std::cerr << msg << '\n';
114 #endif
115 
116 #ifdef _WIN32
117  EnableConsoleOutput();
118 #endif
119 
120  try {
121  randomize(); // seed global random generator
122  initializeSDL();
123 
125  Reactor reactor;
126 #ifdef _WIN32
127  ArgumentGenerator arggen;
128  argv = arggen.GetArguments(argc);
129 #endif
130  CommandLineParser parser(reactor);
131  parser.parse(argc, argv);
132  CommandLineParser::ParseStatus parseStatus = parser.getParseStatus();
133 
134  if (parseStatus != CommandLineParser::EXIT) {
135  if (!parser.isHiddenStartup()) {
136  auto& render = reactor.getDisplay().getRenderSettings().getRendererSetting();
137  render.setValue(render.getRestoreValue());
138  // Switching renderer requires events, handle
139  // these events before continuing with the rest
140  // of initialization. This fixes a bug where
141  // you have a '-script bla.tcl' command line
142  // argument where bla.tcl contains a line like
143  // 'ext gfx9000'.
144  reactor.getEventDistributor().deliverEvents();
145  }
146  if (parseStatus != CommandLineParser::TEST) {
147  CliServer cliServer(reactor.getCommandController(),
148  reactor.getEventDistributor(),
149  reactor.getGlobalCliComm());
150  reactor.run(parser);
151  }
152  }
153  } catch (FatalError& e) {
154  std::cerr << "Fatal error: " << e.getMessage() << '\n';
155  exitCode = 1;
156  } catch (MSXException& e) {
157  std::cerr << "Uncaught exception: " << e.getMessage() << '\n';
158  exitCode = 1;
159  } catch (std::exception& e) {
160  std::cerr << "Uncaught std::exception: " << e.what() << '\n';
161  exitCode = 1;
162  } catch (...) {
163  std::cerr << "Uncaught exception of unexpected type." << '\n';
164  exitCode = 1;
165  }
166  // Clean up.
167  if (SDL_WasInit(SDL_INIT_EVERYTHING)) {
168  SDL_Quit();
169  }
170 
171  return exitCode;
172 }
173 
174 } // namespace openmsx
175 
176 // Enter the openMSX namespace.
177 int main(int argc, char **argv)
178 {
179  // TODO with SDL1 we had the comment:
180  // need exit() iso return on win32/SDL
181  // Is that still the case with SDL2? Because for Android we need to
182  // return from main instead of exit().
183  return openmsx::main(argc, argv);
184 }
int main(int argc, char **argv)
Definition: main.cc:177
std::string toString(time_t time)
Definition: Date.cc:150
void setMainThread()
Store ID of the main thread, should be called exactly once from the main thread.
Definition: Thread.cc:9
This file implemented 3 utility functions:
Definition: Autofire.cc:5
int exitCode
Definition: Reactor.cc:62
#define ad_printf(...)
Definition: openmsx.hh:11
void randomize()
Seed the (shared) random number generator.
Definition: random.hh:16