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