1 /*
2  * This file is part of the Colobot: Gold Edition source code
3  * Copyright (C) 2001-2020, Daniel Roux, EPSITEC SA & TerranovaTeam
4  * http://epsitec.ch; http://colobot.info; http://github.com/colobot
5  *
6  * This program is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14  * See the GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program. If not, see http://gnu.org/licenses
18  */
19 
20 /**
21  * \file app/app.h
22  * \brief CApplication class
23  */
24 
25 #pragma once
26 
27 #include "common/event.h"
28 #include "common/language.h"
29 #include "common/singleton.h"
30 
31 #include "graphics/core/device.h"
32 
33 #include "level/level_category.h"
34 
35 
36 #include <string>
37 #include <vector>
38 #include <map>
39 
40 
41 class CEventQueue;
42 class CController;
43 class CSoundInterface;
44 class CInput;
45 class CModManager;
46 class CPathManager;
47 class CConfigFile;
48 class CSystemUtils;
49 struct SystemTimeStamp;
50 
51 namespace Gfx
52 {
53 class CEngine;
54 struct DeviceConfig;
55 }
56 
57 /**
58  * \struct JoystickDevice
59  * \brief Information about a joystick device
60  */
61 struct JoystickDevice
62 {
63     //! Device index (-1 = invalid device)
64     int index;
65     //! Device name
66     std::string name;
67     //! Number of axes (only available after joystick opened)
68     int axisCount;
69     //! Number of buttons (only available after joystick opened)
70     int buttonCount;
71 
JoystickDeviceJoystickDevice72     JoystickDevice()
73         : index(-1), axisCount(0), buttonCount(0) {}
74 };
75 
76 /**
77  * \enum ParseArgsStatus
78  * \brief State of parsing commandline arguments
79  */
80 enum ParseArgsStatus
81 {
82     PARSE_ARGS_OK   = 1, //! < all ok
83     PARSE_ARGS_FAIL = 2, //! < invalid syntax
84     PARSE_ARGS_HELP = 3  //! < -help requested
85 };
86 
87 /**
88  * \enum MouseMode
89  * \brief Mode of mouse cursor
90  */
91 enum MouseMode
92 {
93     MOUSE_SYSTEM, //! < system cursor visible; in-game cursor hidden
94     MOUSE_ENGINE, //! < in-game cursor visible; system cursor hidden
95     MOUSE_BOTH,   //! < both cursors visible (only for debug)
96     MOUSE_NONE,   //! < no cursor visible
97 };
98 
99 enum DebugMode
100 {
101     DEBUG_SYS_EVENTS = 1 << 0,
102     DEBUG_UPDATE_EVENTS = 1 << 1,
103     DEBUG_APP_EVENTS = 1 << 2,
104     DEBUG_EVENTS     = DEBUG_SYS_EVENTS | DEBUG_UPDATE_EVENTS | DEBUG_APP_EVENTS,
105     DEBUG_MODELS     = 1 << 3,
106     DEBUG_ALL        = DEBUG_SYS_EVENTS | DEBUG_APP_EVENTS | DEBUG_MODELS
107 };
108 
109 struct ApplicationPrivate;
110 
111 /**
112  * \class CApplication
113  * \brief Main application
114  *
115  * This class is responsible for main application execution, including creating
116  * and handling main application window, receiving events, etc.
117  *
118  * It is a singleton class with only one instance that can be created.
119  *
120  * \section Creation Creation of other main objects
121  *
122  * The class creates the only instance of CEventQueue, CEngine,
123  * CRobotMain and CSoundInterface classes.
124  *
125  * \section Window Window management
126  *
127  * The class is responsible for creating app window, setting and changing the video mode,
128  * joystick management, grabbing input and changing the system mouse cursor
129  * position and visibility.
130  * ("System mouse cursor" means the cursor displayed by the OS in constrast to the cursor
131  * displayed by CEngine).
132  *
133  * \section Events Events
134  *
135  * Events are taken from SDL event queue, translated to common events from src/common.h
136  * and pushed to global event queue CEventQueue.
137  *
138  * Joystick events are generated somewhat differently, by running a separate timer,
139  * polling the device for changes and synthesising events on change. It avoids flooding
140  * the event queue with too many joystick events and the granularity of the timer can be
141  * adjusted.
142  *
143  * The events are passed to ProcessEvent() of classes in this order: CApplication, CEngine
144  * and CRobotMain. CApplication and CEngine's ProcessEvent() functions return bool, which
145  * means whether to pass the event on, or stop the chain. This is to enable handling some
146  * events which are internal to CApplication or CEngine.
147  *
148  * \section Portability Portability
149  *
150  * Currently, the class only handles OpenGL devices. SDL can be used with DirectX, but
151  * for that to work, video initialization and video setting must be done differently.
152  *
153  */
154 class CApplication : public CSingleton<CApplication>
155 {
156 public:
157     //! Constructor (can only be called once!)
158     CApplication(CSystemUtils* systemUtils);
159     //! Destructor
160     ~CApplication();
161 
162     //! Returns the application's event queue
163     CEventQueue* GetEventQueue();
164     //! Returns the sound subsystem
165     CSoundInterface* GetSound();
166     //! Returns the mod manager
167     CModManager* GetModManager();
168 
169 public:
170     //! Loads some data from environment variables
171     void LoadEnvironmentVariables();
172     //! Parses commandline arguments (they take priority)
173     ParseArgsStatus ParseArguments(int argc, char *argv[]);
174     //! Initializes the application
175     bool        Create();
176     //! Reloads the application resources, e.g. mods
177     void        ReloadResources();
178     //! Main event loop
179     int         Run();
180     //! Returns the code to be returned at main() exit
181     int         GetExitCode() const;
182 
183     //! Returns the message of error (set to something if exit code is not 0)
184     const std::string& GetErrorMessage() const;
185 
186     //! Returns a list of possible video modes
187     void        GetVideoResolutionList(std::vector<Math::IntPoint> &resolutions, int display = 0) const;
188 
189     //! Returns the current video mode
190     Gfx::DeviceConfig GetVideoConfig() const;
191 
192     //! Change the video mode to given mode
193     bool        ChangeVideoConfig(const Gfx::DeviceConfig &newConfig);
194 
195     //! Suspends animation (time will not be updated)
196     void        SuspendSimulation();
197     //! Resumes animation
198     void        ResumeSimulation();
199     //! Returns whether simulation is suspended
200     bool        GetSimulationSuspended() const;
201 
202     //! Resets time counters to account for time spent loading game
203     void        ResetTimeAfterLoading();
204 
205     //@{
206     //! Management of simulation speed
207     void            SetSimulationSpeed(float speed);
208     float           GetSimulationSpeed() const;
209     //@}
210 
211     //! Returns the absolute time counter [seconds]
212     float       GetAbsTime() const;
213     //! Returns the exact absolute time counter [nanoseconds]
214     long long   GetExactAbsTime() const;
215 
216     //! Returns the exact absolute time counter disregarding speed setting [nanoseconds]
217     long long   GetRealAbsTime() const;
218 
219     //! Returns the relative time since last update [seconds]
220     float       GetRelTime() const;
221     //! Returns the exact realative time since last update [nanoseconds]
222     long long   GetExactRelTime() const;
223 
224     //! Returns the exact relative time since last update disregarding speed setting [nanoseconds]
225     long long   GetRealRelTime() const;
226 
227     //! Returns a list of available joystick devices
228     std::vector<JoystickDevice> GetJoystickList() const;
229 
230     //! Returns info about the current joystick
231     JoystickDevice GetJoystick() const;
232 
233     //! Change the current joystick device
234     bool        ChangeJoystick(const JoystickDevice &newJoystick);
235 
236     //! Management of joystick enable state
237     //@{
238     void        SetJoystickEnabled(bool enable);
239     bool        GetJoystickEnabled() const;
240     //@}
241 
242     //! Polls the state of joystick axes and buttons
243     void        UpdateJoystick();
244 
245     //! Updates the mouse position explicitly
246     void        UpdateMouse();
247 
248     //! Management of mouse mode
249     //@{
250     void        SetMouseMode(MouseMode mode);
251     MouseMode   GetMouseMode() const;
252     //@}
253 
254     //! Enable/disable text input, this toggles the on-screen keyboard on some platforms
255     /** This also allows for writing in CJK languages (not tested!), see https://wiki.libsdl.org/Tutorials/TextInput for detailed explanation */
256     void        SetTextInput(bool textInputEnabled, int id);
257 
258     //! Moves (warps) the mouse cursor to the specified position (in interface coords)
259     void        MoveMouse(Math::Point pos);
260 
261     //! Management of debug modes (printing more info in logger)
262     //@{
263     void        SetDebugModeActive(DebugMode mode, bool active);
264     bool        IsDebugModeActive(DebugMode mode) const;
265     static bool ParseDebugModes(const std::string& str, int& debugModes);
266     //@}
267 
268     //! Management of language
269     //@{
270     Language    GetLanguage() const;
271     char        GetLanguageChar() const;
272     void        SetLanguage(Language language);
273     //@}
274 
275     bool        GetSceneTestMode();
276 
277     //! Renders the image in window
278     void        Render();
279 
280     //! Renders the image in window if needed
281     void        RenderIfNeeded(int updateRate);
282 
283     //! Starts a force feedback effect on the joystick
284     void        PlayForceFeedbackEffect(float strength = 1.0f, int length = 999999);
285     //! Stops a force feedback effect on the joystick
286     void        StopForceFeedbackEffect();
287 
288 protected:
289     //! Creates the window's SDL_Surface
290     bool CreateVideoSurface();
291     //! Tries to set the SDL vsync state desired by the 3D engine
292     //! The final state of SDL vsync is set in the 3D engine afterwards
293     void TryToSetVSync();
294 
295     //! Processes the captured SDL event to Event struct
296     Event       ProcessSystemEvent();
297     //! If applicable, creates a virtual event to match the changed state as of new event
298     Event       CreateVirtualEvent(const Event& sourceEvent);
299     //! Prepares a simulation update event
300     TEST_VIRTUAL Event CreateUpdateEvent(SystemTimeStamp *newTimeStamp);
301     //! Logs debug data for event
302     void        LogEvent(const Event& event);
303 
304     //! Opens the joystick device
305     bool OpenJoystick();
306     //! Closes the joystick device
307     void CloseJoystick();
308 
309     //! Internal procedure to reset time counters
310     void InternalResumeSimulation();
311 
312     //! Loads music in a new thread
313     void StartLoadingMusic();
314 
315 protected:
316     //! System utils instance
317     CSystemUtils* m_systemUtils;
318     //! Private (SDL-dependent data)
319     std::unique_ptr<ApplicationPrivate> m_private;
320     //! Global event queue
321     std::unique_ptr<CEventQueue> m_eventQueue;
322     //! Graphics engine
323     std::unique_ptr<Gfx::CEngine> m_engine;
324     //! Graphics device
325     std::unique_ptr<Gfx::CDevice> m_device;
326     //! Sound subsystem
327     std::unique_ptr<CSoundInterface> m_sound;
328     //! Game controller - game engine and UI
329     std::unique_ptr<CController> m_controller;
330     //! Profile (INI) reader/writer
331     std::unique_ptr<CConfigFile> m_configFile;
332     //! Input manager
333     std::unique_ptr<CInput> m_input;
334     //! Path manager
335     std::unique_ptr<CPathManager> m_pathManager;
336     //! Mod manager
337     std::unique_ptr<CModManager> m_modManager;
338 
339     //! Code to return at exit
340     int             m_exitCode;
341     //! Whether application window is active
342     bool            m_active;
343     //! Bit array of active debug modes
344     long            m_debugModes;
345 
346     //! Message to be displayed as error to the user
347     std::string     m_errorMessage;
348 
349     //! Current configuration of OpenGL display device
350     Gfx::DeviceConfig m_deviceConfig;
351 
352     //! Text set as window title
353     std::string     m_windowTitle;
354 
355     //! Animation time stamps, etc.
356     //@{
357     SystemTimeStamp* m_baseTimeStamp;
358     SystemTimeStamp* m_lastTimeStamp;
359     SystemTimeStamp* m_curTimeStamp;
360 
361     long long       m_realAbsTimeBase;
362     long long       m_realAbsTime;
363     long long       m_realRelTime;
364 
365     long long       m_absTimeBase;
366     long long       m_exactAbsTime;
367     long long       m_exactRelTime;
368 
369     float           m_absTime;
370     float           m_relTime;
371 
372     float           m_simulationSpeed;
373     bool            m_simulationSuspended;
374     //@}
375 
376     SystemTimeStamp* m_manualFrameLast;
377     SystemTimeStamp* m_manualFrameTime;
378 
379     //! Graphics device to use
380     bool            m_graphicsOverride = false;
381     std::string     m_graphics = "default";
382     //! OpenGL version to use
383     bool            m_glVersionOverride = false;
384     int             m_glMajor = -1;
385     int             m_glMinor = -1;
386     //! OpenGL profile
387     bool            m_glProfileOverride = false;
388     int             m_glProfile = 0;
389 
390     //! Current mode of mouse
391     MouseMode       m_mouseMode;
392 
393     //! Info about current joystick device
394     JoystickDevice  m_joystick;
395     //! Whether joystick is enabled
396     bool            m_joystickEnabled;
397     //! Current state of joystick axes; may be updated from another thread
398     std::vector<int> m_joyAxeState;
399     //! Current state of joystick buttons; may be updated from another thread
400     std::vector<bool> m_joyButtonState;
401 
402     //@{
403     //! Scene to run on startup
404     LevelCategory   m_runSceneCategory;
405     int             m_runSceneRank;
406     //@}
407 
408     //! Scene test mode
409     bool            m_sceneTest;
410 
411     //! Application language
412     Language        m_language;
413 
414     //! Screen resoultion overriden by commandline
415     bool            m_resolutionOverride;
416 
417     //! Headles mode
418     bool            m_headless;
419 
420     //! Static buffer for putenv locale
421     static char m_languageLocale[50];
422 
423     std::map<int, bool> m_textInputEnabled;
424 };
425