diff options
Diffstat (limited to 'src/app')
-rw-r--r-- | src/app/app.cpp | 688 | ||||
-rw-r--r-- | src/app/app.h | 153 | ||||
-rw-r--r-- | src/app/main.cpp | 41 | ||||
-rw-r--r-- | src/app/system.cpp | 270 | ||||
-rw-r--r-- | src/app/system.h | 46 | ||||
-rw-r--r-- | src/app/system_linux.h | 101 | ||||
-rw-r--r-- | src/app/system_other.h | 146 | ||||
-rw-r--r-- | src/app/system_windows.h | 122 |
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(¤tEvent, 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; +} |