summaryrefslogtreecommitdiffstats
path: root/src/app
diff options
context:
space:
mode:
Diffstat (limited to 'src/app')
-rw-r--r--src/app/app.cpp688
-rw-r--r--src/app/app.h153
-rw-r--r--src/app/main.cpp41
-rw-r--r--src/app/system.cpp270
-rw-r--r--src/app/system.h46
-rw-r--r--src/app/system_linux.h101
-rw-r--r--src/app/system_other.h146
-rw-r--r--src/app/system_windows.h122
8 files changed, 1147 insertions, 420 deletions
diff --git a/src/app/app.cpp b/src/app/app.cpp
index 68131fc..c778a63 100644
--- a/src/app/app.cpp
+++ b/src/app/app.cpp
@@ -20,12 +20,27 @@
#include "app/app.h"
#include "app/system.h"
+#include "common/logger.h"
#include "common/iman.h"
+#include "common/image.h"
+#include "graphics/opengl/gldevice.h"
#include <SDL/SDL.h>
#include <SDL/SDL_image.h>
+#include <stdio.h>
+
+
+template<> CApplication* CSingleton<CApplication>::mInstance = NULL;
+
+
+//! Interval of timer called to update joystick state
+const int JOYSTICK_TIMER_INTERVAL = 1000/30;
+
+//! Function called by the timer
+Uint32 JoystickTimerCallback(Uint32 interval, void *);
+
/**
* \struct ApplicationPrivate
@@ -42,57 +57,57 @@ struct ApplicationPrivate
//! Joystick
SDL_Joystick *joystick;
//! Index of joystick device
- int joystickDevice;
+ int joystickIndex;
+ //! Id of joystick timer
+ SDL_TimerID joystickTimer;
+ //! Current configuration of OpenGL display device
+ Gfx::GLDeviceConfig deviceConfig;
ApplicationPrivate()
{
memset(&currentEvent, 0, sizeof(SDL_Event));
surface = NULL;
joystick = NULL;
- joystickDevice = 0;
+ joystickIndex = 0;
+ joystickTimer = 0;
}
};
+
CApplication::CApplication()
{
m_private = new ApplicationPrivate();
m_exitCode = 0;
m_iMan = new CInstanceManager();
- m_event = new CEvent(m_iMan);
- m_engine = 0;
- m_robotMain = 0;
- m_sound = 0;
+ m_eventQueue = new CEventQueue(m_iMan);
+
+ m_engine = NULL;
+ m_device = NULL;
+ m_robotMain = NULL;
+ m_sound = NULL;
m_keyState = 0;
m_axeKey = Math::Vector(0.0f, 0.0f, 0.0f);
m_axeJoy = Math::Vector(0.0f, 0.0f, 0.0f);
- m_vidMemTotal = 0;
- m_active = false;
- m_activateApp = false;
- m_ready = false;
- m_joystick = false;
- m_time = 0.0f;
+ m_active = false;
+ m_activateApp = false;
+ m_ready = false;
+ m_joystickEnabled = false;
- for (int i = 0; i < 32; i++)
- {
- m_joyButton[i] = false;
- }
+ m_time = 0.0f;
m_windowTitle = "COLOBOT";
- m_appUseZBuffer = true;
- m_appUseStereo = true;
m_showStats = false;
m_debugMode = false;
- m_audioState = true;
- m_audioTrack = true;
- m_niceMouse = false;
m_setupMode = true;
+ m_dataPath = "./data";
+
ResetKey();
}
@@ -101,91 +116,102 @@ CApplication::~CApplication()
delete m_private;
m_private = NULL;
+ delete m_eventQueue;
+ m_eventQueue = NULL;
+
delete m_iMan;
m_iMan = NULL;
}
-Error CApplication::ParseArguments(int argc, char *argv[])
+bool CApplication::ParseArguments(int argc, char *argv[])
{
+ bool waitDataDir = false;
+
for (int i = 1; i < argc; ++i)
{
std::string arg = argv[i];
+ if (waitDataDir)
+ {
+ waitDataDir = false;
+ m_dataPath = arg;
+ }
+
if (arg == "-debug")
{
m_showStats = true;
SetDebugMode(true);
}
- else if (arg == "-audiostate")
+ else if (arg == "-datadir")
{
- m_audioState = false;
+ waitDataDir = true;
}
- else if (arg == "-audiotrack")
+ else
{
- m_audioTrack = false;
+ m_exitCode = 1;
+ return false;
}
- // TODO else {} report invalid argument
}
- return ERR_OK;
+ // Data dir not given?
+ if (waitDataDir)
+ return false;
+
+ return true;
}
bool CApplication::Create()
{
-/*
-TODO
- Full screen by default unless in debug mode
- if (! m_debugMode)
- m_deviceConfig.fullScreen = true;
-
- int full = 0;
- if (GetProfileInt("Device", "FullScreen", full))
- m_deviceConfig.fullScreen = full == 1;
-*/
+ // TODO: verify that data directory exists
// Temporarily -- only in windowed mode
- m_deviceConfig.fullScreen = false;
+ m_private->deviceConfig.fullScreen = false;
-/*
-TODO
- // Create the 3D engine.
- m_engine = new CEngine(m_iMan, this);
+ // Create the 3D engine
+ m_engine = new Gfx::CEngine(m_iMan, this);
- // Initialize the app's custom scene stuff
- if (! m_engine->OneTimeSceneInit())
- {
- SystemDialog(SDT_ERROR, "COLOBOT - Error", m_engine->RetError());
- return false;
- }
- // Create the sound instance.
+/* // Create the sound instance.
m_sound = new CSound(m_iMan);
// Create the robot application.
- m_robotMain = new CRobotMain(m_iMan);
-*/
+ m_robotMain = new CRobotMain(m_iMan); */
- Uint32 initFlags = SDL_INIT_VIDEO;
- if (m_joystick)
- initFlags |= SDL_INIT_JOYSTICK;
+
+ /* SDL initialization sequence */
+
+
+ Uint32 initFlags = SDL_INIT_VIDEO | SDL_INIT_JOYSTICK | SDL_INIT_TIMER;
if (SDL_Init(initFlags) < 0)
{
- SystemDialog( SDT_ERROR, "COLOBOT - Error", "SDL initialization error:\n" + std::string(SDL_GetError()) );
+ SystemDialog( SDT_ERROR, "COLOBOT - Error", "SDL initialization error:\n" +
+ std::string(SDL_GetError()) );
+ m_exitCode = 2;
+ return false;
+ }
+
+ if ((IMG_Init(IMG_INIT_PNG) & IMG_INIT_PNG) == 0)
+ {
+ SystemDialog( SDT_ERROR, "COLOBOT - Error", std::string("SDL_Image initialization error:\n") +
+ std::string(IMG_GetError()) );
+ m_exitCode = 3;
return false;
}
const SDL_VideoInfo *videoInfo = SDL_GetVideoInfo();
- if (! videoInfo)
+ if (videoInfo == NULL)
{
- SystemDialog( SDT_ERROR, "COLOBOT - Error", "SDL error while getting video info:\n " + std::string(SDL_GetError()) );
+ SystemDialog( SDT_ERROR, "COLOBOT - Error", "SDL error while getting video info:\n " +
+ std::string(SDL_GetError()) );
+ m_exitCode = 2;
return false;
}
Uint32 videoFlags = SDL_OPENGL | SDL_GL_DOUBLEBUFFER | SDL_HWPALETTE;
- if (m_deviceConfig.resizeable)
+ if (m_private->deviceConfig.resizeable)
videoFlags |= SDL_RESIZABLE;
// Use hardware surface if available
@@ -198,68 +224,77 @@ TODO
if (videoInfo->blit_hw)
videoFlags |= SDL_HWACCEL;
- if (m_deviceConfig.fullScreen)
+ if (m_private->deviceConfig.fullScreen)
videoFlags |= SDL_FULLSCREEN;
- SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
- SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
- SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
- SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);
+ // Set OpenGL attributes
- SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
- SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
+ SDL_GL_SetAttribute(SDL_GL_RED_SIZE, m_private->deviceConfig.redSize);
+ SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, m_private->deviceConfig.greenSize);
+ SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, m_private->deviceConfig.blueSize);
+ SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, m_private->deviceConfig.alphaSize);
- if ((IMG_Init(IMG_INIT_PNG) & IMG_INIT_PNG) == 0)
- {
- SystemDialog( SDT_ERROR, "COLOBOT - Error", std::string("SDL_Image initialization error:\n") +
- std::string(IMG_GetError()) );
- return false;
- }
+ SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, m_private->deviceConfig.depthSize);
- m_private->surface = SDL_SetVideoMode(m_deviceConfig.width, m_deviceConfig.height,
- m_deviceConfig.bpp, videoFlags);
+ if (m_private->deviceConfig.doubleBuf)
+ SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
- if (! m_private->surface)
+ /* If hardware acceleration specifically requested, this will force the hw accel
+ and fail with error if not available */
+ if (m_private->deviceConfig.hardwareAccel)
+ SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1);
+
+ m_private->surface = SDL_SetVideoMode(m_private->deviceConfig.width, m_private->deviceConfig.height,
+ m_private->deviceConfig.bpp, videoFlags);
+
+ if (m_private->surface == NULL)
{
SystemDialog( SDT_ERROR, "COLOBT - Error", std::string("SDL error while setting video mode:\n") +
std::string(SDL_GetError()) );
+ m_exitCode = 2;
return false;
}
SDL_WM_SetCaption(m_windowTitle.c_str(), m_windowTitle.c_str());
+ // Enable translating key codes of key press events to unicode chars
SDL_EnableUNICODE(1);
+ // Don't generate joystick events
+ SDL_JoystickEventState(SDL_IGNORE);
+
-/*
-TODO
+ // For now, enable joystick for testing
+ SetJoystickEnabled(true);
- InitJoystick();
- if ( !GetProfileInt("Setup", "Sound3D", b3D) )
+ // The video is ready, we can create and initalize the graphics device
+ m_device = new Gfx::CGLDevice();
+ if (! m_device->Create() )
{
- b3D = true;
+ SystemDialog( SDT_ERROR, "COLOBT - Error", std::string("Error in CDevice::Create() :\n") +
+ std::string(m_device->GetError()) );
+ m_exitCode = 1;
+ return false;
}
- m_pSound->SetDebugMode(m_bDebugMode);
- m_pSound->Create(m_hWnd, b3D);
- m_pSound->CacheAll();
- m_pSound->SetState(m_bAudioState);
- m_pSound->SetAudioTrack(m_bAudioTrack);
- m_pSound->SetCDpath(m_CDpath);
-
- // First execution?
- if ( !GetProfileInt("Setup", "ObjectDirty", iValue) )
+
+ m_engine->SetDevice(m_device);
+ if (! m_engine->Create() )
{
- m_pD3DEngine->FirstExecuteAdapt(true);
+ SystemDialog( SDT_ERROR, "COLOBT - Error", std::string("Error in CEngine::Create() :\n") +
+ std::string(m_engine->GetError()) );
+ m_exitCode = 1;
+ return false;
}
- // Creates the file colobot.ini at the first execution.
- m_pRobotMain->CreateIni();
-
- m_pRobotMain->ChangePhase(PHASE_WELCOME2);
+ if (! m_engine->AfterDeviceSetInit() )
+ {
+ SystemDialog( SDT_ERROR, "COLOBT - Error", std::string("Error in CEngine::AfterDeviceSetInit() :\n") +
+ std::string(m_engine->GetError()) );
+ m_exitCode = 1;
+ return false;
+ }
- m_engine->TimeInit();
-*/
// The app is ready to go
m_ready = true;
@@ -269,66 +304,222 @@ TODO
void CApplication::Destroy()
{
+ /*if (m_robotMain != NULL)
+ {
+ delete m_robotMain;
+ m_robotMain = NULL;
+ }
+
+ if (m_sound != NULL)
+ {
+ delete m_sound;
+ m_sound = NULL;
+ }*/
+
+ if (m_engine != NULL)
+ {
+ if (m_engine->GetWasInit())
+ m_engine->Destroy();
+
+ delete m_engine;
+ m_engine = NULL;
+ }
+
+ if (m_device != NULL)
+ {
+ if (m_device->GetWasInit())
+ m_device->Destroy();
+
+ delete m_device;
+ m_device = NULL;
+ }
+
if (m_private->joystick != NULL)
{
SDL_JoystickClose(m_private->joystick);
m_private->joystick = NULL;
}
- SDL_FreeSurface(m_private->surface);
- m_private->surface = NULL;
+ if (m_private->surface != NULL)
+ {
+ SDL_FreeSurface(m_private->surface);
+ m_private->surface = NULL;
+ }
IMG_Quit();
SDL_Quit();
}
-int CApplication::Run()
+bool CApplication::OpenJoystick()
{
- m_active = true;
+ m_private->joystick = SDL_JoystickOpen(m_private->joystickIndex);
+ if (m_private->joystick == NULL)
+ return false;
+
+ // Create the vectors with joystick axis & button states to exactly the required size
+ m_joyAxeState = std::vector<int>(SDL_JoystickNumAxes(m_private->joystick), 0);
+ m_joyButtonState = std::vector<bool>(SDL_JoystickNumButtons(m_private->joystick), false);
+
+ // Create a timer for polling joystick state
+ m_private->joystickTimer = SDL_AddTimer(JOYSTICK_TIMER_INTERVAL, JoystickTimerCallback, NULL);
+
+ return true;
+}
+
- while (m_private->currentEvent.type != SDL_QUIT)
+void CApplication::CloseJoystick()
+{
+ // Timer will remove itself automatically
+
+ SDL_JoystickClose(m_private->joystick);
+ m_private->joystick = NULL;
+}
+
+Uint32 JoystickTimerCallback(Uint32 interval, void *)
+{
+ CApplication *app = CApplication::GetInstancePointer();
+ if ((app == NULL) || (! app->GetJoystickEnabled()))
+ return 0; // don't run the timer again
+
+ app->UpdateJoystick();
+
+ return interval; // run for the same interval again
+}
+
+/** Updates the state info in CApplication and on change, creates SDL events and pushes them to SDL event queue.
+ This way, the events get handled properly in the main event loop and besides, SDL_PushEvent() ensures thread-safety. */
+void CApplication::UpdateJoystick()
+{
+ if (! m_joystickEnabled)
+ return;
+
+ SDL_JoystickUpdate();
+
+ for (int axis = 0; axis < (int) m_joyAxeState.size(); ++axis)
{
- // Use SDL_PeepEvents() if the app is active, so we can use idle time to
- // render the scene. Else, use SDL_PollEvent() to avoid eating CPU time.
- int count = 0;
- if (m_active)
+ int newValue = SDL_JoystickGetAxis(m_private->joystick, axis);
+
+ if (m_joyAxeState[axis] != newValue)
{
- SDL_PumpEvents();
- count = SDL_PeepEvents(&m_private->currentEvent, 1, SDL_GETEVENT, SDL_ALLEVENTS);
+ m_joyAxeState[axis] = newValue;
+
+ SDL_Event joyAxisEvent;
+
+ joyAxisEvent.jaxis.type = SDL_JOYAXISMOTION;
+ joyAxisEvent.jaxis.which = 0;
+ joyAxisEvent.jaxis.axis = axis;
+ joyAxisEvent.jaxis.value = newValue;
+
+ SDL_PushEvent(&joyAxisEvent);
}
- else
+ }
+
+ for (int button = 0; button < (int) m_joyButtonState.size(); ++button)
+ {
+ bool newValue = SDL_JoystickGetButton(m_private->joystick, button) == 1;
+
+ if (m_joyButtonState[button] != newValue)
{
- SDL_PollEvent(&m_private->currentEvent);
+ m_joyButtonState[button] = newValue;
+
+ SDL_Event joyButtonEvent;
+
+ if (newValue)
+ {
+ joyButtonEvent.jbutton.type = SDL_JOYBUTTONDOWN;
+ joyButtonEvent.jbutton.state = SDL_PRESSED;
+ }
+ else
+ {
+ joyButtonEvent.jbutton.type = SDL_JOYBUTTONUP;
+ joyButtonEvent.jbutton.state = SDL_RELEASED;
+ }
+ joyButtonEvent.jbutton.which = 0;
+ joyButtonEvent.jbutton.button = button;
+
+ SDL_PushEvent(&joyButtonEvent);
}
+ }
+}
+
+int CApplication::Run()
+{
+ m_active = true;
+
+ while (true)
+ {
+ // To be sure no old event remains
+ m_private->currentEvent.type = SDL_NOEVENT;
- // If received an event
- if ((m_active && count > 0) || (!m_active))
+ if (m_active)
+ SDL_PumpEvents();
+
+ bool haveEvent = true;
+ while (haveEvent)
{
- ParseEvent();
+ haveEvent = false;
+
+ int count = 0;
+ // Use SDL_PeepEvents() if the app is active, so we can use idle time to
+ // render the scene. Else, use SDL_PollEvent() to avoid eating CPU time.
+ if (m_active)
+ count = SDL_PeepEvents(&m_private->currentEvent, 1, SDL_GETEVENT, SDL_ALLEVENTS);
+ else
+ count = SDL_PollEvent(&m_private->currentEvent);
+
+ // If received an event
+ if (count > 0)
+ {
+ haveEvent = true;
+
+ Event event = ParseEvent();
+
+ if (event.type == EVENT_QUIT)
+ goto end; // exit the loop
+
+ if (event.type != EVENT_NULL)
+ {
+ bool passOn = ProcessEvent(event);
+
+ if (m_engine != NULL && passOn)
+ passOn = m_engine->ProcessEvent(event);
+
+ if (passOn)
+ m_eventQueue->AddEvent(event);
+ }
+ }
}
- // Render a frame during idle time (no messages are waiting)
+ // Enter game update & frame rendering only if active
if (m_active && m_ready)
{
Event event;
- while (m_event->GetEvent(event))
+ while (m_eventQueue->GetEvent(event))
{
- if (event.event == EVENT_QUIT)
- {
+ if (event.type == EVENT_QUIT)
goto end; // exit both loops
+
+ bool passOn = true;
+
+ // Skip system events (they have been processed earlier)
+ if (! event.systemEvent)
+ {
+ passOn = ProcessEvent(event);
+
+ if (passOn && m_engine != NULL)
+ passOn = m_engine->ProcessEvent(event);
}
- //m_robotMain->EventProcess(event);
+ /*if (passOn && m_robotMain != NULL)
+ m_robotMain->ProcessEvent(event); */
}
- //if ( !RetNiceMouse())
- //{
- // SetMouseType(m_engine->RetMouseType());
- //}
+ // Update game and render a frame during idle time (no messages are waiting)
+ bool ok = Render();
// If an error occurs, push quit event to the queue
- if (! Render())
+ if (! ok)
{
SDL_Event quitEvent;
memset(&quitEvent, 0, sizeof(SDL_Event));
@@ -339,7 +530,6 @@ int CApplication::Run()
}
end:
- //m_sound->StopMusic();
Destroy();
return m_exitCode;
@@ -350,74 +540,181 @@ int CApplication::GetExitCode()
return m_exitCode;
}
-void CApplication::ParseEvent()
+//! Translates SDL press state to PressState
+PressState TranslatePressState(unsigned char state)
{
-/* Event event;
+ if (state == SDL_PRESSED)
+ return STATE_PRESSED;
+ else
+ return STATE_RELEASED;
+}
- if (m_private->currentEvent.type == SDL_MOUSEBUTTONDOWN)
+/** Conversion of the position of the mouse from window coords to interface coords:
+ - x: 0=left, 1=right
+ - y: 0=down, 1=up */
+Math::Point CApplication::WindowToInterfaceCoords(Math::IntPoint pos)
+{
+ return Math::Point( (float)pos.x / (float)m_private->deviceConfig.width,
+ 1.0f - (float)pos.y / (float)m_private->deviceConfig.height);
+}
+
+Math::IntPoint CApplication::InterfaceToWindowCoords(Math::Point pos)
+{
+ return Math::IntPoint((int)(pos.x * m_private->deviceConfig.width),
+ (int)((1.0f - pos.y) * m_private->deviceConfig.height));
+}
+
+/** The SDL event parsed is stored internally.
+ If event is not available or is not understood, returned event is of type EVENT_NULL. */
+Event CApplication::ParseEvent()
+{
+ Event event;
+
+ event.systemEvent = true;
+
+ if (m_private->currentEvent.type == SDL_QUIT)
{
- if (m_private->currentEvent.button.button == SDL_BUTTON_LEFT)
- event.event = EVENT_LBUTTONDOWN;
- else if (m_private->currentEvent.button.button == SDL_BUTTON_RIGHT)
- event.event = EVENT_RBUTTONDOWN;
+ event.type = EVENT_QUIT;
}
- else if (m_private->currentEvent.type == SDL_MOUSEBUTTONUP)
+ else if ( (m_private->currentEvent.type == SDL_KEYDOWN) ||
+ (m_private->currentEvent.type == SDL_KEYUP) )
{
- if (m_private->currentEvent.button.button == SDL_BUTTON_LEFT)
- event.event = EVENT_LBUTTONUP;
- else if (m_private->currentEvent.button.button == SDL_BUTTON_RIGHT)
- event.event = EVENT_RBUTTONUP;
+ if (m_private->currentEvent.type == SDL_KEYDOWN)
+ event.type = EVENT_KEY_DOWN;
+ else
+ event.type = EVENT_KEY_UP;
+
+ event.key.key = m_private->currentEvent.key.keysym.sym;
+ event.key.mod = m_private->currentEvent.key.keysym.mod;
+ event.key.state = TranslatePressState(m_private->currentEvent.key.state);
+ event.key.unicode = m_private->currentEvent.key.keysym.unicode;
}
- else if (m_private->currentEvent.type == SDL_MOUSEMOTION)
+ else if ( (m_private->currentEvent.type == SDL_MOUSEBUTTONDOWN) ||
+ (m_private->currentEvent.type == SDL_MOUSEBUTTONUP) )
{
- event.event = EVENT_MOUSEMOVE;
+ if (m_private->currentEvent.type == SDL_MOUSEBUTTONDOWN)
+ event.type = EVENT_MOUSE_BUTTON_DOWN;
+ else
+ event.type = EVENT_MOUSE_BUTTON_UP;
+
+ event.mouseButton.button = m_private->currentEvent.button.button;
+ event.mouseButton.state = TranslatePressState(m_private->currentEvent.button.state);
+ event.mouseButton.pos = WindowToInterfaceCoords(Math::IntPoint(m_private->currentEvent.button.x, m_private->currentEvent.button.y));
}
- else if (m_private->currentEvent.type == SDL_KEYDOWN)
+ else if (m_private->currentEvent.type == SDL_MOUSEMOTION)
{
- event.event = EVENT_KEYDOWN;
+ event.type = EVENT_MOUSE_MOVE;
+
+ event.mouseMove.state = TranslatePressState(m_private->currentEvent.button.state);
+ event.mouseMove.pos = WindowToInterfaceCoords(Math::IntPoint(m_private->currentEvent.button.x, m_private->currentEvent.button.y));
}
- else if (m_private->currentEvent.type == SDL_KEYUP)
+ else if (m_private->currentEvent.type == SDL_JOYAXISMOTION)
{
- event.event = EVENT_KEYUP;
- }
+ event.type = EVENT_JOY_AXIS;
- if (m_robotMain != NULL && event.event != EVENT_NULL)
- {
- m_robotMain->EventProcess(event);
+ event.joyAxis.axis = m_private->currentEvent.jaxis.axis;
+ event.joyAxis.value = m_private->currentEvent.jaxis.value;
}
- if (m_engine != NULL)
+ else if ( (m_private->currentEvent.type == SDL_JOYBUTTONDOWN) ||
+ (m_private->currentEvent.type == SDL_JOYBUTTONUP) )
{
- m_engine->MsgProc( hWnd, uMsg, wParam, lParam );
+ if (m_private->currentEvent.type == SDL_JOYBUTTONDOWN)
+ event.type = EVENT_JOY_BUTTON_DOWN;
+ else
+ event.type = EVENT_JOY_BUTTON_UP;
+
+ event.joyButton.button = m_private->currentEvent.jbutton.button;
+ event.joyButton.state = TranslatePressState(m_private->currentEvent.jbutton.state);
}
- ProcessEvent(event);*/
+ return event;
}
-void CApplication::ProcessEvent(Event event)
+/** Processes incoming events. It is the first function called after an event is captures.
+ Function returns \c true if the event is to be passed on to other processing functions
+ or \c false if not. */
+bool CApplication::ProcessEvent(const Event &event)
{
+ CLogger *l = GetLogger();
+ // Print the events in debug mode to test the code
+ if (m_debugMode)
+ {
+ switch (event.type)
+ {
+ case EVENT_KEY_DOWN:
+ case EVENT_KEY_UP:
+ l->Info("EVENT_KEY_%s:\n", (event.type == EVENT_KEY_DOWN) ? "DOWN" : "UP");
+ l->Info(" key = %4x\n", event.key.key);
+ l->Info(" state = %s\n", (event.key.state == STATE_PRESSED) ? "STATE_PRESSED" : "STATE_RELEASED");
+ l->Info(" mod = %4x\n", event.key.mod);
+ l->Info(" unicode = %4x\n", event.key.unicode);
+ break;
+ case EVENT_MOUSE_MOVE:
+ l->Info("EVENT_MOUSE_MOVE:\n");
+ l->Info(" state = %s\n", (event.mouseMove.state == STATE_PRESSED) ? "STATE_PRESSED" : "STATE_RELEASED");
+ l->Info(" pos = (%f, %f)\n", event.mouseMove.pos.x, event.mouseMove.pos.y);
+ break;
+ case EVENT_MOUSE_BUTTON_DOWN:
+ case EVENT_MOUSE_BUTTON_UP:
+ l->Info("EVENT_MOUSE_BUTTON_%s:\n", (event.type == EVENT_MOUSE_BUTTON_DOWN) ? "DOWN" : "UP");
+ l->Info(" button = %d\n", event.mouseButton.button);
+ l->Info(" state = %s\n", (event.mouseButton.state == STATE_PRESSED) ? "STATE_PRESSED" : "STATE_RELEASED");
+ l->Info(" pos = (%f, %f)\n", event.mouseButton.pos.x, event.mouseButton.pos.y);
+ break;
+ case EVENT_JOY_AXIS:
+ l->Info("EVENT_JOY_AXIS:\n");
+ l->Info(" axis = %d\n", event.joyAxis.axis);
+ l->Info(" value = %d\n", event.joyAxis.value);
+ break;
+ case EVENT_JOY_BUTTON_DOWN:
+ case EVENT_JOY_BUTTON_UP:
+ l->Info("EVENT_JOY_BUTTON_%s:\n", (event.type == EVENT_JOY_BUTTON_DOWN) ? "DOWN" : "UP");
+ l->Info(" button = %d\n", event.joyButton.button);
+ l->Info(" state = %s\n", (event.joyButton.state == STATE_PRESSED) ? "STATE_PRESSED" : "STATE_RELEASED");
+ break;
+ default:
+ break;
+ }
+ }
+ // By default, pass on all events
+ return true;
}
+/** Renders the frame and swaps buffers as necessary. Returns \c false on error. */
bool CApplication::Render()
{
bool result = m_engine->Render();
if (! result)
return false;
- if (m_deviceConfig.doubleBuf)
+ if (m_private->deviceConfig.doubleBuf)
SDL_GL_SwapBuffers();
return true;
}
+/** Called in to toggle the pause state of the app. */
void CApplication::Pause(bool pause)
{
- // TODO
-}
+ static long appPausedCount = 0L;
-void CApplication::SetMousePos(Math::Point pos)
-{
- // TODO
+ appPausedCount += ( pause ? +1 : -1 );
+ m_ready = appPausedCount == 0;
+
+ // Handle the first pause request (of many, nestable pause requests)
+ if( pause && ( 1 == appPausedCount ) )
+ {
+ // Stop the scene from animating
+ //m_engine->TimeEnterGel();
+ }
+
+ // Final pause request done
+ if (appPausedCount == 0)
+ {
+ // Restart the scene
+ //m_engine->TimeExitGel();
+ }
}
void CApplication::StepSimulation(float rTime)
@@ -425,32 +722,29 @@ void CApplication::StepSimulation(float rTime)
// TODO
}
-void SetShowStat(bool show)
+void CApplication::SetShowStat(bool show)
{
- // TODO
+ m_showStats = show;
}
-bool CApplication::RetShowStat()
+bool CApplication::GetShowStat()
{
- // TODO
- return false;
+ return m_showStats;
}
void CApplication::SetDebugMode(bool mode)
{
- // TODO
+ m_debugMode = mode;
}
-bool CApplication::RetDebugMode()
+bool CApplication::GetDebugMode()
{
- // TODO
- return false;
+ return m_debugMode;
}
-bool CApplication::RetSetupMode()
+bool CApplication::GetSetupMode()
{
- // TODO
- return false;
+ return m_setupMode;
}
void CApplication::FlushPressKey()
@@ -468,46 +762,73 @@ void CApplication::SetKey(int keyRank, int option, int key)
// TODO
}
-int CApplication::RetKey(int keyRank, int option)
+int CApplication::GetKey(int keyRank, int option)
{
// TODO
return 0;
}
-void CApplication::SetJoystick(bool enable)
+void CApplication::SetGrabInput(bool grab)
{
- // TODO
+ SDL_WM_GrabInput(grab ? SDL_GRAB_ON : SDL_GRAB_OFF);
}
-bool CApplication::RetJoystick()
+bool CApplication::GetGrabInput()
{
- // TODO
- return false;
+ int result = SDL_WM_GrabInput(SDL_GRAB_QUERY);
+ return result == SDL_GRAB_ON;
}
-void SetMouseType(Gfx::MouseType type)
+void CApplication::SetSystemMouseVisible(bool visible)
{
- // TODO
+ SDL_ShowCursor(visible ? SDL_ENABLE : SDL_DISABLE);
}
-void SetNiceMouse(bool nice)
+bool CApplication::GetSystemMouseVisibile()
{
- // TODO
+ int result = SDL_ShowCursor(SDL_QUERY);
+ return result == SDL_ENABLE;
}
-bool CApplication::RetNiceMouse()
+
+void CApplication::SetSystemMousePos(Math::Point pos)
{
- return false;
+ Math::IntPoint windowPos = InterfaceToWindowCoords(pos);
+ SDL_WarpMouse(windowPos.x, windowPos.y);
+ m_systemMousePos = pos;
}
-bool CApplication::RetNiceMouseCap()
+Math::Point CApplication::GetSystemMousePos()
{
- return false;
+ return m_systemMousePos;
+}
+
+void CApplication::SetJoystickEnabled(bool enable)
+{
+ m_joystickEnabled = enable;
+
+ if (m_joystickEnabled)
+ {
+ if (! OpenJoystick())
+ {
+ m_joystickEnabled = false;
+ }
+ }
+ else
+ {
+ CloseJoystick();
+ }
+}
+
+bool CApplication::GetJoystickEnabled()
+{
+ return m_joystickEnabled;
}
bool CApplication::WriteScreenShot(char *filename, int width, int height)
{
// TODO
+ return false;
}
void CApplication::InitText()
@@ -529,3 +850,8 @@ void CApplication::OutputText(long x, long y, char* str)
{
// TODO
}
+
+std::string CApplication::GetDataFilePath(const std::string& dirName, const std::string& fileName)
+{
+ return m_dataPath + "/" + dirName + "/" + fileName;
+}
diff --git a/src/app/app.h b/src/app/app.h
index f776b10..956eab8 100644
--- a/src/app/app.h
+++ b/src/app/app.h
@@ -21,10 +21,12 @@
#include "common/misc.h"
-#include "graphics/common/device.h"
-#include "graphics/common/engine.h"
+#include "common/singleton.h"
+#include "graphics/core/device.h"
+#include "graphics/engine/engine.h"
#include <string>
+#include <vector>
class CInstanceManager;
@@ -32,19 +34,41 @@ class CEvent;
class CRobotMain;
class CSound;
-struct ApplicationPrivate;
+struct ApplicationPrivate;
/**
* \class CApplication
* \brief Main application
*
- * This class is responsible for creating and handling main application window,
- * receiving events, etc.
+ * This class is responsible for main application execution, including creating
+ * and handling main application window, receiving events, etc.
+ *
+ * It is a singleton class with only one instance that can be created.
+ *
+ * Creation of other main objects
+ *
+ * The class creates the only instance of CInstanceManager, CEventQueue, CEngine,
+ * CRobotMain and CSound classes.
+ *
+ * Window management
+ *
+ * The class is responsible for creating app window, setting and changing the video mode,
+ * setting the position of mouse and changing the cursor, grabbing and writing screenshots.
+ *
+ * Events
+ *
+ * Events are taken from SDL event queue and either handled by CApplication or translated
+ * to common events from src/common.h and pushed to global event queue CEventQueue.
+ * Joystick events are generated somewhat differently, by running a separate timer,
+ * polling the device for changes and synthesising events on change. It avoids flooding
+ * the event queue with too many joystick events and the granularity of the timer can be
+ * adjusted.
+ *
+ * The events are further handled in CRobotMain class.
*
- * ...
*/
-class CApplication
+class CApplication : public CSingleton<CApplication>
{
public:
//! Constructor (can only be called once!)
@@ -54,61 +78,79 @@ public:
public:
//! Parses commandline arguments
- Error ParseArguments(int argc, char *argv[]);
+ bool ParseArguments(int argc, char *argv[]);
//! Initializes the application
bool Create();
//! Main event loop
int Run();
-
//! Returns the code to be returned at main() exit
int GetExitCode();
-protected:
//! Cleans up before exit
void Destroy();
- //! Processes an SDL event to Event struct
- void ParseEvent();
- //! Handles some incoming events
- void ProcessEvent(Event event);
- //! Renders the image in window
- bool Render();
-public:
+ //! Enters the pause mode
void Pause(bool pause);
+
+ //! Updates the simulation state
void StepSimulation(float rTime);
- void SetMousePos(Math::Point pos);
+ //! Polls the state of joystick axes and buttons
+ void UpdateJoystick();
void SetShowStat(bool show);
- bool RetShowStat();
+ bool GetShowStat();
+
void SetDebugMode(bool mode);
- bool RetDebugMode();
- bool RetSetupMode();
+ bool GetDebugMode();
+
+ bool GetSetupMode();
+
+ void SetJoystickEnabled(bool enable);
+ bool GetJoystickEnabled();
void FlushPressKey();
void ResetKey();
void SetKey(int keyRank, int option, int key);
- int RetKey(int keyRank, int option);
+ int GetKey(int keyRank, int option);
+
+ //! Sets the grab mode for input (keyboard & mouse)
+ void SetGrabInput(bool grab);
+ //! Returns the grab mode
+ bool GetGrabInput();
- void SetJoystick(bool enable);
- bool RetJoystick();
+ //! Sets the visiblity of system mouse cursor
+ void SetSystemMouseVisible(bool visible);
+ //! Returns the visiblity of system mouse cursor
+ bool GetSystemMouseVisibile();
- void SetMouseType(Gfx::MouseType type);
- void SetNiceMouse(bool nice);
- bool RetNiceMouse();
- bool RetNiceMouseCap();
+ //! Sets the position of system mouse cursor (in interface coords)
+ void SetSystemMousePos(Math::Point pos);
+ //! Returns the position of system mouse cursor (in interface coords)
+ Math::Point GetSystemMousePos();
bool WriteScreenShot(char *filename, int width, int height);
+ //! Returns the full path to a file in data directory
+ std::string GetDataFilePath(const std::string &dirName, const std::string &fileName);
+
protected:
- //HRESULT ConfirmDevice( DDCAPS* pddDriverCaps, D3DDEVICEDESC7* pd3dDeviceDesc );
- //HRESULT Initialize3DEnvironment();
- //HRESULT Change3DEnvironment();
- //HRESULT CreateZBuffer(GUID* pDeviceGUID);
- //HRESULT Render3DEnvironment();
- //VOID Cleanup3DEnvironment();
- //VOID DeleteDeviceObjects();
- //VOID DisplayFrameworkError( HRESULT, DWORD );
+ //! Processes the captured SDL event to Event struct
+ Event ParseEvent();
+ //! Handles some incoming events
+ bool ProcessEvent(const Event &event);
+ //! Renders the image in window
+ bool Render();
+
+ //! Opens the joystick device
+ bool OpenJoystick();
+ //! Closes the joystick device
+ void CloseJoystick();
+
+ //! Converts window coords to interface coords
+ Math::Point WindowToInterfaceCoords(Math::IntPoint pos);
+ //! Converts the interface coords to window coords
+ Math::IntPoint InterfaceToWindowCoords(Math::Point pos);
void InitText();
void DrawSuppl();
@@ -116,14 +158,20 @@ protected:
void OutputText(long x, long y, char* str);
protected:
+ //! Instance manager
+ CInstanceManager* m_iMan;
//! Private (SDL-dependent data)
ApplicationPrivate* m_private;
- CInstanceManager* m_iMan;
- Gfx::DeviceConfig m_deviceConfig;
+ //! Global event queue
+ CEventQueue* m_eventQueue;
+ //! Graphics engine
Gfx::CEngine* m_engine;
- CEvent* m_event;
- CRobotMain* m_robotMain;
+ //! Graphics device
+ Gfx::CDevice* m_device;
+ //! Sound subsystem
CSound* m_sound;
+ //! Main class of the proper game engine
+ CRobotMain* m_robotMain;
//! Code to return at exit
int m_exitCode;
@@ -131,27 +179,32 @@ protected:
bool m_active;
bool m_activateApp;
bool m_ready;
- bool m_joystick;
- std::string m_windowTitle;
- long m_vidMemTotal;
- bool m_appUseZBuffer;
- bool m_appUseStereo;
bool m_showStats;
bool m_debugMode;
- bool m_audioState;
- bool m_audioTrack;
- bool m_niceMouse;
bool m_setupMode;
+ //! Whether joystick is enabled
+ bool m_joystickEnabled;
+
+ //! Text set as window title
+ std::string m_windowTitle;
+
int m_keyState;
Math::Vector m_axeKey;
Math::Vector m_axeJoy;
- bool m_joyButton[32];
- Math::Point m_mousePos;
+ Math::Point m_systemMousePos;
long m_mouseWheel;
+ //! Current state of joystick axes; may be updated from another thread
+ std::vector<int> m_joyAxeState;
+ //! Current state of joystick buttons; may be updated from another thread
+ std::vector<bool> m_joyButtonState;
+
float m_time;
long m_key[50][2];
+
+ //! Path to directory with data files
+ std::string m_dataPath;
};
diff --git a/src/app/main.cpp b/src/app/main.cpp
index 151cd20..9eea6e4 100644
--- a/src/app/main.cpp
+++ b/src/app/main.cpp
@@ -24,6 +24,42 @@
#include "common/restext.h"
+/* Doxygen main page */
+
+/**
+
+\mainpage
+
+Doxygen documentation of Colobot project
+
+\section Intro Introduction
+
+The source code released by Epitec was sparsely documented. This documentation, written from scratch,
+will aim to describe the various components of the code.
+
+Currently, the only documented classes are the ones written from scratch or the old ones rewritten to match the new code.
+In time, the documentation will be extended to cover every major part of the code.
+
+\section Structure Code structure
+
+The source code was split from the original all-in-one directory to subdirectories, each containing one major part of the project.
+The current layout is this:
+ - src/CBot - separate library with CBot language
+ - src/app - class CApplication and everything concerned with SDL plus other system-dependent code such as displaying a message box, finding files, etc.
+ - src/common - shared structs, enums, defines, etc.; should not have any external dependencies
+ - src/graphics/common - interface of graphics engine (CEngine) and device (CDevice), without concrete implementation, shared structs such as Vertex, Material, etc., “effects” classes: CCamera, CLight, CParticle that will use the graphics engine interface
+ - src/graphics/opengl - concrete implementation of CEngine and CDevice classes in OpenGL: CGLEngine and CGLDevice
+ - src/graphics/d3d - in (far) future - perhaps a newer implementation in DirectX (9? 10?)
+ - src/math - mathematical structures and functions
+ - src/object - non-graphical game engine, that is robots, buildings, etc.; dependent only on interface of graphics engine, not on concrete implementation
+ - src/ui - 2D user interface (menu, buttons, check boxes, etc.); also without dependencies to concrete implementation of graphics engine
+ - src/sound - sound and music engine written using fmod library
+ - src/physics - physics engine
+ - src/script - link with the CBot library
+ - src/metafile - separate program for packing data files to .dat format
+*/
+
+
//! Entry point to the program
int main(int argc, char *argv[])
{
@@ -33,16 +69,17 @@ int main(int argc, char *argv[])
CApplication app; // single instance of the application
- Error err = app.ParseArguments(argc, argv);
- if (err != ERR_OK)
+ if (! app.ParseArguments(argc, argv))
{
SystemDialog(SDT_ERROR, "COLOBOT", "Invalid commandline arguments!\n");
+ return app.GetExitCode();
}
int code = 0;
if (! app.Create())
{
+ app.Destroy(); // ensure a clean exit
code = app.GetExitCode();
logger.Info("Didn't run main loop. Exiting with code %d\n", code);
return code;
diff --git a/src/app/system.cpp b/src/app/system.cpp
index a765e11..eb0321b 100644
--- a/src/app/system.cpp
+++ b/src/app/system.cpp
@@ -21,19 +21,21 @@
#include "common/config.h"
+
#if defined(PLATFORM_WINDOWS)
-#include <windows.h>
+#include "system_windows.h"
+
#elif defined(PLATFORM_LINUX)
-#include <cstdlib>
+#include "system_linux.h"
+
#else
-#include <iostream>
+#include "system_other.h"
+
#endif
+#include <cassert>
-SystemDialogResult SystemDialog_Windows(SystemDialogType type, const std::string& title, const std::string& message);
-SystemDialogResult SystemDialog_Linux(SystemDialogType type, const std::string& title, const std::string& message);
-SystemDialogResult SystemDialog_Other(SystemDialogType type, const std::string& title, const std::string& message);
/**
* Displays a system dialog with info, error, question etc. message.
@@ -45,209 +47,103 @@ SystemDialogResult SystemDialog_Other(SystemDialogType type, const std::string&
*/
SystemDialogResult SystemDialog(SystemDialogType type, const std::string& title, const std::string& message)
{
- #if defined(PLATFORM_WINDOWS)
+#if defined(PLATFORM_WINDOWS)
return SystemDialog_Windows(type, title, message);
- #elif defined(PLATFORM_LINUX)
+#elif defined(PLATFORM_LINUX)
return SystemDialog_Linux(type, title, message);
- #else
+#else
return SystemDialog_Other(type, title, message);
- #endif
+#endif
}
-
-
-#if defined(PLATFORM_WINDOWS)
-
-// Convert a wide Unicode string to an UTF8 string
-std::string UTF8_Encode_Windows(const std::wstring &wstr)
+SystemTimeStamp* CreateTimeStamp()
{
- int size_needed = WideCharToMultiByte(CP_UTF8, 0, &wstr[0], (int)wstr.size(), NULL, 0, NULL, NULL);
- std::string strTo(size_needed, 0);
- WideCharToMultiByte(CP_UTF8, 0, &wstr[0], (int)wstr.size(), &strTo[0], size_needed, NULL, NULL);
- return strTo;
+ return new SystemTimeStamp();
}
-// Convert an UTF8 string to a wide Unicode String
-std::wstring UTF8_Decode_Windows(const std::string &str)
+void DestroyTimeStamp(SystemTimeStamp *stamp)
{
- int size_needed = MultiByteToWideChar(CP_UTF8, 0, &str[0], (int)str.size(), NULL, 0);
- std::wstring wstrTo(size_needed, 0);
- MultiByteToWideChar(CP_UTF8, 0, &str[0], (int)str.size(), &wstrTo[0], size_needed);
- return wstrTo;
+ delete stamp;
}
-SystemDialogResult SystemDialog_Windows(SystemDialogType type, const std::string& title, const std::string& message)
+void CopyTimeStamp(SystemTimeStamp *dst, SystemTimeStamp *src)
{
- unsigned int windowsType = 0;
- std::wstring windowsMessage = UTF8_Decode_Windows(message);
- std::wstring windowsTitle = UTF8_Decode_Windows(title);
-
- switch (type)
- {
- case SDT_INFO:
- default:
- windowsType = MB_ICONINFORMATION|MB_OK;
- break;
- case SDT_WARNING:
- windowsType = MB_ICONWARNING|MB_OK;
- break;
- case SDT_ERROR:
- windowsType = MB_ICONERROR|MB_OK;
- break;
- case SDT_YES_NO:
- windowsType = MB_ICONQUESTION|MB_YESNO;
- break;
- case SDT_OK_CANCEL:
- windowsType = MB_ICONWARNING|MB_OKCANCEL;
- break;
- }
-
- switch (MessageBoxW(NULL, windowsMessage.c_str(), windowsTitle.c_str(), windowsType))
- {
- case IDOK:
- return SDR_OK;
- case IDCANCEL:
- return SDR_CANCEL;
- case IDYES:
- return SDR_YES;
- case IDNO:
- return SDR_NO;
- default:
- break;
- }
-
- return SDR_OK;
+ *dst = *src;
}
+void GetCurrentTimeStamp(SystemTimeStamp *stamp)
+{
+#if defined(PLATFORM_WINDOWS)
+ GetCurrentTimeStamp_Windows(stamp);
#elif defined(PLATFORM_LINUX)
+ GetCurrentTimeStamp_Linux(stamp);
+#else
+ GetCurrentTimeStamp_Other(stamp);
+#endif
+}
-SystemDialogResult SystemDialog_Linux(SystemDialogType type, const std::string& title, const std::string& message)
+float GetTimeStampResolution(SystemTimeUnit unit)
{
- std::string options = "";
- switch (type)
- {
- case SDT_INFO:
- default:
- options = "--info";
- break;
- case SDT_WARNING:
- options = "--warning";
- break;
- case SDT_ERROR:
- options = "--error";
- break;
- case SDT_YES_NO:
- options = "--question --ok-label=\"Yes\" --cancel-label=\"No\"";
- break;
- case SDT_OK_CANCEL:
- options = "--question --ok-label=\"OK\" --cancel-label=\"Cancel\"";
- break;
- }
-
- std::string command = "zenity " + options + " --text=\"" + message + "\" --title=\"" + title + "\"";
- int code = system(command.c_str());
-
- SystemDialogResult result = SDR_OK;
- switch (type)
- {
- case SDT_YES_NO:
- result = code ? SDR_NO : SDR_YES;
- break;
- case SDT_OK_CANCEL:
- result = code ? SDR_CANCEL : SDR_OK;
- break;
- default:
- break;
- }
-
+ unsigned long long exact = 0;
+#if defined(PLATFORM_WINDOWS)
+ exact = GetTimeStampExactResolution_Windows();
+#elif defined(PLATFORM_LINUX)
+ exact = GetTimeStampExactResolution_Linux();
+#else
+ exact = GetTimeStampExactResolution_Other();
+#endif
+ float result = 0.0f;
+ if (unit == STU_SEC)
+ result = exact * 1e-9;
+ else if (unit == STU_MSEC)
+ result = exact * 1e-6;
+ else if (unit == STU_USEC)
+ result = exact * 1e-3;
+ else
+ assert(false);
return result;
}
+long long GetTimeStampExactResolution()
+{
+#if defined(PLATFORM_WINDOWS)
+ return GetTimeStampExactResolution_Windows();
+#elif defined(PLATFORM_LINUX)
+ return GetTimeStampExactResolution_Linux();
#else
+ return GetTimeStampExactResolution_Other();
+#endif
+}
-SystemDialogResult SystemDialog_Other(SystemDialogType type, const std::string& title, const std::string& message)
+float TimeStampDiff(SystemTimeStamp *before, SystemTimeStamp *after, SystemTimeUnit unit)
{
- switch (type)
- {
- case SDT_INFO:
- std::cout << "INFO: ";
- break;
- case SDT_WARNING:
- std::cout << "WARNING:";
- break;
- case SDT_ERROR:
- std::cout << "ERROR: ";
- break;
- case SDT_YES_NO:
- case SDT_OK_CANCEL:
- std::cout << "QUESTION: ";
- break;
- }
-
- std::cout << message << std::endl;
-
- std::string line;
-
- SystemDialogResult result = SDR_OK;
-
- bool done = false;
- while (!done)
- {
- switch (type)
- {
- case SDT_INFO:
- case SDT_WARNING:
- case SDT_ERROR:
- std::cout << "Press ENTER to continue";
- break;
-
- case SDT_YES_NO:
- std::cout << "Type 'Y' for Yes or 'N' for No";
- break;
-
- case SDT_OK_CANCEL:
- std::cout << "Type 'O' for OK or 'C' for Cancel";
- break;
- }
-
- std::getline(std::cin, line);
-
- switch (type)
- {
- case SDT_INFO:
- case SDT_WARNING:
- case SDT_ERROR:
- done = true;
- break;
-
- case SDT_YES_NO:
- if (line == "Y" || line == "y")
- {
- result = SDR_YES;
- done = true;
- }
- else if (line == "N" || line == "n")
- {
- result = SDR_NO;
- done = true;
- }
- break;
-
- case SDT_OK_CANCEL:
- if (line == "O" || line == "o")
- {
- done = true;
- result = SDR_OK;
- }
- else if (line == "C" || line == "c")
- {
- done = true;
- result = SDR_CANCEL;
- }
- break;
- }
- }
-
+ long long exact = 0;
+#if defined(PLATFORM_WINDOWS)
+ exact = TimeStampExactDiff_Windows(before, after);
+#elif defined(PLATFORM_LINUX)
+ exact = TimeStampExactDiff_Linux(before, after);
+#else
+ exact = TimeStampExactDiff_Other(before, after);
+#endif
+ float result = 0.0f;
+ if (unit == STU_SEC)
+ result = exact * 1e-9;
+ else if (unit == STU_MSEC)
+ result = exact * 1e-6;
+ else if (unit == STU_USEC)
+ result = exact * 1e-3;
+ else
+ assert(false);
return result;
}
-#endif // if defined(PLATFORM_WINDOWS)
+
+long long TimeStampExactDiff(SystemTimeStamp *before, SystemTimeStamp *after)
+{
+#if defined(PLATFORM_WINDOWS)
+ return TimeStampExactDiff_Windows(before, after);
+#elif defined(PLATFORM_LINUX)
+ return TimeStampExactDiff_Linux(before, after);
+#else
+ return TimeStampExactDiff_Other(before, after);
+#endif
+}
diff --git a/src/app/system.h b/src/app/system.h
index 3bf6457..3c04760 100644
--- a/src/app/system.h
+++ b/src/app/system.h
@@ -23,6 +23,8 @@
#include <string>
+/* Dialog utils */
+
/**
* \enum SysDialogType
* \brief Type of system dialog
@@ -57,3 +59,47 @@ enum SystemDialogResult
//! Displays a system dialog
SystemDialogResult SystemDialog(SystemDialogType, const std::string &title, const std::string &message);
+
+
+/* Time utils */
+
+enum SystemTimeUnit
+{
+ //! seconds
+ STU_SEC,
+ //! milliseconds
+ STU_MSEC,
+ //! microseconds
+ STU_USEC
+};
+
+/* Forward declaration of time stamp struct
+ * SystemTimeStamp should be used in a pointer context.
+ * The implementation details are hidden because of platform dependence. */
+struct SystemTimeStamp;
+
+//! Creates a new time stamp object
+SystemTimeStamp* CreateTimeStamp();
+
+//! Destroys a time stamp object
+void DestroyTimeStamp(SystemTimeStamp *stamp);
+
+//! Copies the time stamp from \a src to \a dst
+void CopyTimeStamp(SystemTimeStamp *dst, SystemTimeStamp *src);
+
+//! Returns a time stamp associated with current time
+void GetCurrentTimeStamp(SystemTimeStamp *stamp);
+
+//! Returns the platform's expected time stamp resolution
+float GetTimeStampResolution(SystemTimeUnit unit = STU_SEC);
+
+//! Returns the platform's exact (in nanosecond units) expected time stamp resolution
+long long GetTimeStampExactResolution();
+
+//! Returns a difference between two timestamps in given time unit
+/** The difference is \a after - \a before. */
+float TimeStampDiff(SystemTimeStamp *before, SystemTimeStamp *after, SystemTimeUnit unit = STU_SEC);
+
+//! Returns the exact (in nanosecond units) difference between two timestamps
+/** The difference is \a after - \a before. */
+long long TimeStampExactDiff(SystemTimeStamp *before, SystemTimeStamp *after);
diff --git a/src/app/system_linux.h b/src/app/system_linux.h
new file mode 100644
index 0000000..f58c9a1
--- /dev/null
+++ b/src/app/system_linux.h
@@ -0,0 +1,101 @@
+// * This file is part of the COLOBOT source code
+// * Copyright (C) 2001-2008, Daniel ROUX & EPSITEC SA, www.epsitec.ch
+// * Copyright (C) 2012, Polish Portal of Colobot (PPC)
+// *
+// * This program is free software: you can redistribute it and/or modify
+// * it under the terms of the GNU General Public License as published by
+// * the Free Software Foundation, either version 3 of the License, or
+// * (at your option) any later version.
+// *
+// * This program is distributed in the hope that it will be useful,
+// * but WITHOUT ANY WARRANTY; without even the implied warranty of
+// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// * GNU General Public License for more details.
+// *
+// * You should have received a copy of the GNU General Public License
+// * along with this program. If not, see http://www.gnu.org/licenses/.
+
+// system_linux.h
+
+/* This header contains Linux-specific code for system utils
+ from system.h. There is no separate .cpp module for simplicity.*/
+
+#include <sys/time.h>
+#include <time.h>
+#include <stdlib.h>
+
+
+SystemDialogResult SystemDialog_Linux(SystemDialogType type, const std::string& title, const std::string& message);
+
+void GetCurrentTimeStamp_Linux(SystemTimeStamp *stamp);
+long long GetTimeStampExactResolution_Linux();
+long long TimeStampExactDiff_Linux(SystemTimeStamp *before, SystemTimeStamp *after);
+
+struct SystemTimeStamp
+{
+ timespec clockTime;
+
+ SystemTimeStamp()
+ {
+ clockTime.tv_sec = clockTime.tv_nsec = 0;
+ }
+};
+
+
+SystemDialogResult SystemDialog_Linux(SystemDialogType type, const std::string& title, const std::string& message)
+{
+ std::string options = "";
+ switch (type)
+ {
+ case SDT_INFO:
+ default:
+ options = "--info";
+ break;
+ case SDT_WARNING:
+ options = "--warning";
+ break;
+ case SDT_ERROR:
+ options = "--error";
+ break;
+ case SDT_YES_NO:
+ options = "--question --ok-label=\"Yes\" --cancel-label=\"No\"";
+ break;
+ case SDT_OK_CANCEL:
+ options = "--question --ok-label=\"OK\" --cancel-label=\"Cancel\"";
+ break;
+ }
+
+ std::string command = "zenity " + options + " --text=\"" + message + "\" --title=\"" + title + "\"";
+ int code = system(command.c_str());
+
+ SystemDialogResult result = SDR_OK;
+ switch (type)
+ {
+ case SDT_YES_NO:
+ result = code ? SDR_NO : SDR_YES;
+ break;
+ case SDT_OK_CANCEL:
+ result = code ? SDR_CANCEL : SDR_OK;
+ break;
+ default:
+ break;
+ }
+
+ return result;
+}
+
+void GetCurrentTimeStamp_Linux(SystemTimeStamp *stamp)
+{
+ clock_gettime(CLOCK_MONOTONIC, &stamp->clockTime);
+}
+
+long long GetTimeStampExactResolution_Linux()
+{
+ return 1ll;
+}
+
+long long TimeStampExactDiff_Linux(SystemTimeStamp *before, SystemTimeStamp *after)
+{
+ return (after->clockTime.tv_nsec - before->clockTime.tv_nsec) +
+ (after->clockTime.tv_sec - before->clockTime.tv_sec) * 1000000000ll;
+}
diff --git a/src/app/system_other.h b/src/app/system_other.h
new file mode 100644
index 0000000..9f13ffa
--- /dev/null
+++ b/src/app/system_other.h
@@ -0,0 +1,146 @@
+// * This file is part of the COLOBOT source code
+// * Copyright (C) 2001-2008, Daniel ROUX & EPSITEC SA, www.epsitec.ch
+// * Copyright (C) 2012, Polish Portal of Colobot (PPC)
+// *
+// * This program is free software: you can redistribute it and/or modify
+// * it under the terms of the GNU General Public License as published by
+// * the Free Software Foundation, either version 3 of the License, or
+// * (at your option) any later version.
+// *
+// * This program is distributed in the hope that it will be useful,
+// * but WITHOUT ANY WARRANTY; without even the implied warranty of
+// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// * GNU General Public License for more details.
+// *
+// * You should have received a copy of the GNU General Public License
+// * along with this program. If not, see http://www.gnu.org/licenses/.
+
+// system_other.h
+
+/* This header contains fallback code for other platforms for system utils
+ from system.h. There is no separate .cpp module for simplicity.*/
+
+#include <SDL/SDL.h>
+
+#include <iostream>
+
+
+SystemDialogResult SystemDialog_Other(SystemDialogType type, const std::string& title, const std::string& message);
+
+void GetCurrentTimeStamp_Other(SystemTimeStamp *stamp);
+long long GetTimeStampExactResolution_Other();
+long long TimeStampExactDiff_Other(SystemTimeStamp *before, SystemTimeStamp *after);
+
+struct SystemTimeStamp
+{
+ Uint32 sdlTicks;
+
+ SystemTimeStamp()
+ {
+ sdlTicks = 0;
+ }
+};
+
+
+SystemDialogResult SystemDialog_Other(SystemDialogType type, const std::string& title, const std::string& message)
+{
+ switch (type)
+ {
+ case SDT_INFO:
+ std::cout << "INFO: ";
+ break;
+ case SDT_WARNING:
+ std::cout << "WARNING:";
+ break;
+ case SDT_ERROR:
+ std::cout << "ERROR: ";
+ break;
+ case SDT_YES_NO:
+ case SDT_OK_CANCEL:
+ std::cout << "QUESTION: ";
+ break;
+ }
+
+ std::cout << message << std::endl;
+
+ std::string line;
+
+ SystemDialogResult result = SDR_OK;
+
+ bool done = false;
+ while (!done)
+ {
+ switch (type)
+ {
+ case SDT_INFO:
+ case SDT_WARNING:
+ case SDT_ERROR:
+ std::cout << "Press ENTER to continue";
+ break;
+
+ case SDT_YES_NO:
+ std::cout << "Type 'Y' for Yes or 'N' for No";
+ break;
+
+ case SDT_OK_CANCEL:
+ std::cout << "Type 'O' for OK or 'C' for Cancel";
+ break;
+ }
+
+ std::getline(std::cin, line);
+
+ switch (type)
+ {
+ case SDT_INFO:
+ case SDT_WARNING:
+ case SDT_ERROR:
+ done = true;
+ break;
+
+ case SDT_YES_NO:
+ if (line == "Y" || line == "y")
+ {
+ result = SDR_YES;
+ done = true;
+ }
+ else if (line == "N" || line == "n")
+ {
+ result = SDR_NO;
+ done = true;
+ }
+ break;
+
+ case SDT_OK_CANCEL:
+ if (line == "O" || line == "o")
+ {
+ done = true;
+ result = SDR_OK;
+ }
+ else if (line == "C" || line == "c")
+ {
+ done = true;
+ result = SDR_CANCEL;
+ }
+ break;
+ }
+ }
+
+ return result;
+}
+
+
+
+void GetCurrentTimeStamp_Other(SystemTimeStamp *stamp)
+{
+ stamp->sdlTicks = SDL_GetTicks();
+}
+
+long long GetTimeStampExactResolution_Other()
+{
+ return 1000000ll;
+}
+
+long long TimeStampExactDiff_Other(SystemTimeStamp *before, SystemTimeStamp *after)
+{
+ return (after->sdlTicks - before->sdlTicks) * 1000000ll;
+}
diff --git a/src/app/system_windows.h b/src/app/system_windows.h
new file mode 100644
index 0000000..eb6beec
--- /dev/null
+++ b/src/app/system_windows.h
@@ -0,0 +1,122 @@
+// * This file is part of the COLOBOT source code
+// * Copyright (C) 2001-2008, Daniel ROUX & EPSITEC SA, www.epsitec.ch
+// * Copyright (C) 2012, Polish Portal of Colobot (PPC)
+// *
+// * This program is free software: you can redistribute it and/or modify
+// * it under the terms of the GNU General Public License as published by
+// * the Free Software Foundation, either version 3 of the License, or
+// * (at your option) any later version.
+// *
+// * This program is distributed in the hope that it will be useful,
+// * but WITHOUT ANY WARRANTY; without even the implied warranty of
+// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// * GNU General Public License for more details.
+// *
+// * You should have received a copy of the GNU General Public License
+// * along with this program. If not, see http://www.gnu.org/licenses/.
+
+// system_windows.h
+
+/* This header contains Windows-specific code for system utils
+ from system.h. There is no separate .cpp module for simplicity.*/
+
+#include <windows.h>
+
+
+std::string UTF8_Encode_Windows(const std::wstring &wstr);
+std::wstring UTF8_Decode_Windows(const std::string &str);
+SystemDialogResult SystemDialog_Windows(SystemDialogType type, const std::string& title, const std::string& message);
+
+void GetCurrentTimeStamp_Windows(SystemTimeStamp *stamp);
+long long GetTimeStampExactResolution_Windows();
+long long TimeStampExactDiff_Windows(SystemTimeStamp *before, SystemTimeStamp *after);
+
+struct SystemTimeStamp
+{
+ FILETIME fileTime;
+
+ SystemTimeStamp()
+ {
+ fileTime.dwHighDateTime = fileTime.dwLowDateTime = 0;
+ }
+};
+
+
+// Convert a wide Unicode string to an UTF8 string
+std::string UTF8_Encode_Windows(const std::wstring &wstr)
+{
+ int size_needed = WideCharToMultiByte(CP_UTF8, 0, &wstr[0], (int)wstr.size(), NULL, 0, NULL, NULL);
+ std::string strTo(size_needed, 0);
+ WideCharToMultiByte(CP_UTF8, 0, &wstr[0], (int)wstr.size(), &strTo[0], size_needed, NULL, NULL);
+ return strTo;
+}
+
+// Convert an UTF8 string to a wide Unicode String
+std::wstring UTF8_Decode_Windows(const std::string &str)
+{
+ int size_needed = MultiByteToWideChar(CP_UTF8, 0, &str[0], (int)str.size(), NULL, 0);
+ std::wstring wstrTo(size_needed, 0);
+ MultiByteToWideChar(CP_UTF8, 0, &str[0], (int)str.size(), &wstrTo[0], size_needed);
+ return wstrTo;
+}
+
+SystemDialogResult SystemDialog_Windows(SystemDialogType type, const std::string& title, const std::string& message)
+{
+ unsigned int windowsType = 0;
+ std::wstring windowsMessage = UTF8_Decode_Windows(message);
+ std::wstring windowsTitle = UTF8_Decode_Windows(title);
+
+ switch (type)
+ {
+ case SDT_INFO:
+ default:
+ windowsType = MB_ICONINFORMATION|MB_OK;
+ break;
+ case SDT_WARNING:
+ windowsType = MB_ICONWARNING|MB_OK;
+ break;
+ case SDT_ERROR:
+ windowsType = MB_ICONERROR|MB_OK;
+ break;
+ case SDT_YES_NO:
+ windowsType = MB_ICONQUESTION|MB_YESNO;
+ break;
+ case SDT_OK_CANCEL:
+ windowsType = MB_ICONWARNING|MB_OKCANCEL;
+ break;
+ }
+
+ switch (MessageBoxW(NULL, windowsMessage.c_str(), windowsTitle.c_str(), windowsType))
+ {
+ case IDOK:
+ return SDR_OK;
+ case IDCANCEL:
+ return SDR_CANCEL;
+ case IDYES:
+ return SDR_YES;
+ case IDNO:
+ return SDR_NO;
+ default:
+ break;
+ }
+
+ return SDR_OK;
+}
+
+
+void GetCurrentTimeStamp_Windows(SystemTimeStamp *stamp)
+{
+ GetSystemTimeAsFileTime(&stamp->fileTime);
+}
+
+long long GetTimeStampExactResolution_Windows()
+{
+ return 100ll;
+}
+
+long long TimeStampExactDiff_Windows(SystemTimeStamp *before, SystemTimeStamp *after)
+{
+ long long tH = (1ll << 32) * (after->fileTime.dwHighDateTime - before->fileTime.dwHighDateTime);
+ long long tL = after->fileTime.dwLowDateTime - before->fileTime.dwLowDateTime;
+ return (tH + tL) * 100ll;
+}