1 /** @file sys_system.cpp Abstract interfaces for platform specific services.
2 *
3 * @authors Copyright © 2003-2017 Jaakko Keränen <jaakko.keranen@iki.fi>
4 * @authors Copyright © 2006-2015 Daniel Swanson <danij@dengine.net>
5 * @authors Copyright © 2006 Jamie Jones <jamie_jones_au@yahoo.com.au>
6 *
7 * @par License
8 * GPL: http://www.gnu.org/licenses/gpl.html
9 *
10 * <small>This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by the
12 * Free Software Foundation; either version 2 of the License, or (at your
13 * option) any later version. This program is distributed in the hope that it
14 * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
15 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
16 * Public License for more details. You should have received a copy of the GNU
17 * General Public License along with this program; if not, see:
18 * http://www.gnu.org/licenses</small>
19 */
20
21 #ifdef WIN32
22 # define WIN32_LEAN_AND_MEAN
23 # include <windows.h>
24 # include <process.h>
25 #endif
26
27 #include <signal.h>
28 #ifdef MACOSX
29 # include <QDir>
30 #endif
31 #ifdef WIN32
32 # include <QSettings>
33 #endif
34
35 #include <de/concurrency.h>
36 #include <de/timer.h>
37 #include <de/App>
38 #include <de/PackageLoader>
39 #include <de/Loop>
40 #include <doomsday/doomsdayapp.h>
41 #include <doomsday/console/exec.h>
42
43 #ifdef __CLIENT__
44 # include "clientapp.h"
45 # include "ui/inputsystem.h"
46 # include "gl/gl_main.h"
47 #endif
48
49 #include "dd_main.h"
50 #include "dd_loop.h"
51 #include "network/net_main.h"
52 #include "network/net_buf.h"
53 #include "ui/nativeui.h"
54 #include "api_base.h"
55
56 #if defined(WIN32) && !defined(_DEBUG)
57 # define DENG_CATCH_SIGNALS
58 #endif
59
60 int novideo; // if true, stay in text mode for debugging
61
62 #ifdef DENG_CATCH_SIGNALS
63 /**
64 * Borrowed from Lee Killough.
65 */
handler(int s)66 static void C_DECL handler(int s)
67 {
68 signal(s, SIG_IGN); // Ignore future instances of this signal.
69
70 App_Error(s==SIGSEGV ? "Segmentation Violation\n" :
71 s==SIGINT ? "Interrupted by User\n" :
72 s==SIGILL ? "Illegal Instruction\n" :
73 s==SIGFPE ? "Floating Point Exception\n" :
74 s==SIGTERM ? "Killed\n" : "Terminated by signal\n");
75 }
76 #endif
77
78 /**
79 * Initialize platform level services.
80 *
81 * \note This must be called from the main thread due to issues with the devices
82 * we use via the WINAPI, MCI (cdaudio, mixer etc) on the WIN32 platform.
83 */
Sys_Init()84 void Sys_Init()
85 {
86 de::Time begunAt;
87
88 LOG_VERBOSE("Setting up platform state...");
89
90 App_AudioSystem().initPlayback();
91
92 #ifdef DENG_CATCH_SIGNALS
93 // Register handler for abnormal situations (in release build).
94 signal(SIGSEGV, handler);
95 signal(SIGTERM, handler);
96 signal(SIGILL, handler);
97 signal(SIGFPE, handler);
98 signal(SIGILL, handler);
99 signal(SIGABRT, handler);
100 #endif
101
102 #ifndef WIN32
103 // We are not worried about broken pipes. When a TCP connection closes,
104 // we prefer to receive an error code instead of a signal.
105 signal(SIGPIPE, SIG_IGN);
106 #endif
107
108 LOG_NET_VERBOSE("Initializing Network subsystem...");
109 N_Init();
110
111 LOGDEV_VERBOSE("Sys_Init completed in %.2f seconds") << begunAt.since();
112 }
113
Sys_IsShuttingDown()114 bool Sys_IsShuttingDown()
115 {
116 return DoomsdayApp::app().isShuttingDown();
117 }
118
119 /**
120 * Return to default system state.
121 */
Sys_Shutdown()122 void Sys_Shutdown()
123 {
124 // We are now shutting down.
125 DoomsdayApp::app().setShuttingDown(true);
126
127 // Time to unload *everything*.
128 if(App_GameLoaded())
129 Con_Execute(CMDS_DDAY, "unload", true, false);
130
131 // Unload all loaded packages.
132 de::App::packageLoader().unloadAll();
133
134 // Deinitialize all subsystems.
135 Net_Shutdown();
136
137 #ifdef __CLIENT__
138 if (ClientApp::hasAudioSystem())
139 {
140 // Let's shut down sound first, so Windows' HD-hogging doesn't jam
141 // the MUS player (would produce horrible bursts of notes).
142 App_AudioSystem().deinitPlayback();
143 }
144 GL_Shutdown();
145 if(ClientApp::hasInputSystem())
146 {
147 ClientApp::inputSystem().clearEvents();
148 }
149 #endif
150
151 App_ClearGames();
152 }
153
showCriticalMessage(char const * msg)154 static int showCriticalMessage(char const *msg)
155 {
156 // This is going to be the end, I'm afraid.
157 de::Loop::get().stop();
158
159 #if defined (__CLIENT__)
160 Sys_MessageBox(MBT_WARNING, DOOMSDAY_NICENAME, msg, 0);
161 #else
162 qWarning() << msg;
163 #endif
164 return 0;
165 }
166
Sys_CriticalMessage(const char * msg)167 int Sys_CriticalMessage(const char* msg)
168 {
169 return showCriticalMessage(msg);
170 }
171
Sys_CriticalMessagef(const char * format,...)172 int Sys_CriticalMessagef(const char* format, ...)
173 {
174 static const char* unknownMsg = "Unknown critical issue occured.";
175 const size_t BUF_SIZE = 655365;
176 const char* msg;
177 char* buf = 0;
178 va_list args;
179 int result;
180
181 if(format && format[0])
182 {
183 va_start(args, format);
184 buf = (char*) calloc(1, BUF_SIZE);
185 dd_vsnprintf(buf, BUF_SIZE, format, args);
186 msg = buf;
187 va_end(args);
188 }
189 else
190 {
191 msg = unknownMsg;
192 }
193
194 result = showCriticalMessage(msg);
195
196 if(buf) free(buf);
197 return result;
198 }
199
Sys_Sleep(int millisecs)200 void Sys_Sleep(int millisecs)
201 {
202 /*
203 #ifdef WIN32
204 Sleep(millisecs);
205 #endif
206 */
207 Thread_Sleep(millisecs);
208 }
209
Sys_BlockUntilRealTime(uint realTimeMs)210 void Sys_BlockUntilRealTime(uint realTimeMs)
211 {
212 uint remaining = realTimeMs - Timer_RealMilliseconds();
213 if(remaining > 50)
214 {
215 // Target time is in the past; or the caller is attempting to wait for
216 // too long a time.
217 return;
218 }
219
220 while(Timer_RealMilliseconds() < realTimeMs)
221 {
222 // Do nothing; don't yield execution. We want to exit here at the
223 // precise right moment.
224 }
225 }
226
Sys_HideMouseCursor()227 void Sys_HideMouseCursor()
228 {
229 #ifdef WIN32
230 if(novideo) return;
231
232 ShowCursor(FALSE);
233 #else
234 // The cursor is controlled using Qt in Canvas.
235 #endif
236 }
237
238 /**
239 * Called when Doomsday should quit (will be deferred until convenient).
240 */
241 #undef Sys_Quit
Sys_Quit(void)242 DENG_EXTERN_C void Sys_Quit(void)
243 {
244 auto &app = DoomsdayApp::app();
245
246 if (app.busyMode().isActive())
247 {
248 // The busy worker is running; we cannot just stop it abruptly.
249 Sys_MessageBox2(MBT_WARNING, DOOMSDAY_NICENAME, "Cannot quit while in busy mode.",
250 "Try again later after the current operation has finished.", 0);
251 return;
252 }
253
254 app.setShuttingDown(true);
255
256 // Unload the game currently loaded. This will ensure it is shut down gracefully.
257 if (!app.game().isNull())
258 {
259 #ifdef __CLIENT__
260 ClientWindow::main().glActivate();
261 BusyMode_FreezeGameForBusyMode();
262 #endif
263 app.changeGame(GameProfiles::null(), DD_ActivateGameWorker);
264 }
265
266 #ifdef __CLIENT__
267 if (ClientWindow::mainExists())
268 {
269 ClientWindow::main().fadeContent(ClientWindow::FadeToBlack, 0.1);
270 de::Loop::get().timer(0.1, [] () { DENG2_APP->stopLoop(DD_GameLoopExitCode()); });
271 }
272 else
273 #endif
274 {
275 // It's time to stop the main loop.
276 DENG2_APP->stopLoop(DD_GameLoopExitCode());
277 }
278 }
279