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