/
RadiantApp.cpp
238 lines (190 loc) · 6.98 KB
/
RadiantApp.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
#include "RadiantApp.h"
#include "i18n.h"
#include "iradiant.h"
#include "version.h"
#include "log/PIDFile.h"
#include "module/CoreModule.h"
#include "messages/GameConfigNeededMessage.h"
#include "ui/prefdialog/GameSetupDialog.h"
#include "module/StaticModule.h"
#include "settings/LocalisationProvider.h"
#include <wx/wxprec.h>
#include <wx/event.h>
#include <wx/cmdline.h>
#include <wx/xrc/xmlres.h>
#include <sigc++/functors/mem_fun.h>
#ifndef __linux__
#include "ui/splash/Splash.h"
#endif
#ifdef POSIX
#include <libintl.h>
#endif
#include <exception>
#include <iomanip>
#if defined (_DEBUG) && defined (WIN32) && defined (_MSC_VER)
#include "crtdbg.h"
#endif
#if defined(__linux__)
// Function to intercept unwanted Gtk log messages on Linux
#include <glib.h>
GLogWriterOutput
log_black_hole(GLogLevelFlags, const GLogField*, gsize, gpointer)
{
return G_LOG_WRITER_HANDLED;
}
#endif
// The startup event which will be queued in App::OnInit()
wxDEFINE_EVENT(EV_RadiantStartup, wxCommandEvent);
bool RadiantApp::OnInit()
{
if (!wxApp::OnInit()) return false;
// Initialise the debug flags
#if defined (_DEBUG) && defined (WIN32) && defined (_MSC_VER)
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
#endif
#if defined(__linux__)
// Intercept and discard Gtk log messages emitted from wxGTK, which we
// cannot control or fix, and which obliterate any attempt to use console
// output for debugging (due to fresh Gtk-CRITICAL messages being emitted
// several times per second)
g_log_set_writer_func(log_black_hole, nullptr, nullptr);
#endif
// Initialise the context (application path / settings path, is
// OS-specific)
_context.initialise(wxApp::argc, wxApp::argv);
try
{
_coreModule.reset(new module::CoreModule(_context));
auto* radiant = _coreModule->get();
module::RegistryReference::Instance().setRegistry(radiant->getModuleRegistry());
module::initialiseStreams(radiant->getLogWriter());
}
catch (module::CoreModule::FailureException& ex)
{
// Streams are not yet initialised, so log to std::err at this point
std::cerr << ex.what() << std::endl;
return false;
}
// Register the localisation helper before initialising the modules
settings::LocalisationProvider::Initialise(_context);
auto& languageManager = _coreModule->get()->getLanguageManager();
languageManager.registerProvider(settings::LocalisationProvider::Instance());
#if defined(POSIX) && !defined(__APPLE__)
// greebo: not sure if this is needed
// Other POSIX gettext initialisation
setlocale(LC_ALL, "");
textdomain(GETTEXT_PACKAGE);
bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR);
#endif
// reset some locale settings back to standard c
// this is e.g. needed for parsing float values from textfiles
setlocale(LC_NUMERIC, "C");
setlocale(LC_TIME, "C");
// Set up art provider, logging, XRC file handlers
initWxWidgets();
// Register to the start up signal
Bind(EV_RadiantStartup, &RadiantApp::onStartupEvent, this);
AddPendingEvent(wxCommandEvent(EV_RadiantStartup));
return true;
}
void RadiantApp::initWxWidgets()
{
// Stop wx's unhelpful debug messages about missing keyboard accel
// strings from cluttering up the console
wxLog::SetLogLevel(wxLOG_Warning);
wxFileSystem::AddHandler(new wxLocalFSHandler);
wxXmlResource::Get()->InitAllHandlers();
// Our XRC resource files are stored in the ui/ folder.
wxXmlResource::Get()->Load(_context.getRuntimeDataPath() + "ui/*.xrc");
wxInitAllImageHandlers();
// Register the local art provider
_bitmapArtProvider = std::make_unique<wxutil::LocalBitmapArtProvider>(_context.getBitmapsPath());
}
void RadiantApp::cleanupWxWidgets()
{
_bitmapArtProvider.reset();
wxImage::CleanUpHandlers();
wxXmlResource::Get()->ClearHandlers();
wxFileSystem::CleanUpHandlers();
}
int RadiantApp::OnExit()
{
// Issue a shutdown() call to all the modules
module::GlobalModuleRegistry().shutdownModules();
auto& languageManager = _coreModule->get()->getLanguageManager();
languageManager.clearProvider();
// Clean up static resources
settings::LocalisationProvider::Cleanup();
_coreModule.reset();
cleanupWxWidgets();
return wxApp::OnExit();
}
void RadiantApp::OnInitCmdLine(wxCmdLineParser& parser)
{
// Remove '/' as parameter starter to allow for "/path" style args
parser.SetSwitchChars("-");
parser.AddLongSwitch("disable-sound", _("Disable sound for this session."));
parser.AddLongOption("verbose", _("Verbose logging."));
parser.AddParam("Map file", wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL);
parser.AddParam("fs_game=<game>", wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL);
parser.AddParam("fs_game_base=<gamebase>", wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL);
}
bool RadiantApp::OnExceptionInMainLoop()
{
try
{
// This method is called by the main loop controlling code,
// from within the catch(...) block. Let's re-throw the current
// exception and catch it to print the error message at the very least.
throw;
}
catch (const std::exception& ex)
{
rError() << "Unhandled Exception: " << ex.what() << std::endl;
}
return wxApp::OnExceptionInMainLoop();
}
void RadiantApp::onStartupEvent(wxCommandEvent& ev)
{
// Create the radiant.pid file in the settings folder
// (emits a warning if the file already exists (due to a previous startup failure))
applog::PIDFile pidFile(PID_FILENAME);
#ifndef __linux__
// We skip the splash screen in Linux, but the other platforms will show a progress bar
// Connect the progress callback to the Splash instance.
ui::Splash::OnAppStartup();
#endif
// In first-startup scenarios the game configuration is not present
// in which case the GameManager will dispatch a message asking
// for showing a dialog or similar. Connect the listener.
_coreModule->get()->getMessageBus().addListener(radiant::IMessage::Type::GameConfigNeeded,
radiant::TypeListener<game::ConfigurationNeeded>(ui::GameSetupDialog::HandleGameConfigMessage));
// Pick up all the statically defined modules and register them
module::internal::StaticModuleList::RegisterModules();
// Register to the modules unloading event, we need to get notified
// before the DLLs/SOs are relased to give wxWidgets a chance to clean up
_modulesUnloadingHandler = _coreModule->get()->getModuleRegistry().signal_modulesUnloading()
.connect(sigc::mem_fun(this, &RadiantApp::onModulesUnloading));
try
{
// Startup the application
_coreModule->get()->startup();
}
catch (const radiant::IRadiant::StartupFailure& ex)
{
// An unhandled exception during module initialisation => display a popup and exit
rError() << "Unhandled Exception: " << ex.what() << std::endl;
wxutil::Messagebox::ShowFatalError(ex.what(), nullptr);
}
// Scope ends here, PIDFile is deleted by its destructor
}
void RadiantApp::onModulesUnloading()
{
// We need to delete all pending objects before unloading modules
// wxWidgets needs a chance to delete them before memory access is denied
if (wxTheApp != nullptr)
{
wxTheApp->ProcessIdle();
}
_modulesUnloadingHandler.disconnect();
}