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