//----------------------------------------------------------------------------- // Copyright (c) 2013 GarageGames, LLC // Portions Copyright (c) 2014 James S Urquhart // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to // deal in the Software without restriction, including without limitation the // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or // sell copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // IN THE SOFTWARE. //----------------------------------------------------------------------------- #include "console/console.h" #include "io/fileStream.h" #include "game/resource.h" #include "game/version.h" #include "math/mRandom.h" #include "platformEmscripten/platformEmscripten.h" #include "platformEmscripten/EmscriptenConsole.h" #include "platform/event.h" #include "game/gameInterface.h" #include "platform/platform.h" #include "platform/platformAL.h" #include "platform/platformInput.h" #include "platform/platformVideo.h" #include "debug/profiler.h" #include "platformEmscripten/platformGL.h" #include "platformEmscripten/EmscriptenOGLVideo.h" #ifndef DEDICATED #include "platformEmscripten/EmscriptenInputManager.h" #endif #include #include #include #include // fork, execvp, chdir #include // nanosleep #ifndef DEDICATED #include #include #include extern bool InitOpenGL(); extern void Cleanup(bool minimal=false); #endif extern "C" { extern U32 _EmscriptenGetDesktopHeight(); extern U32 _EmscriptenGetDesktopWidth(); extern U32 _EmscriptenGetDesktopBpp(); } EmscriptenPlatState::EmscriptenPlatState() { captureDisplay = true; fadeWindows = true; backgrounded = false; minimized = false; quit = false; portrait = true;//-Mat Android is in portrait mode by default // start with something reasonable. fullscreen = true; osVersion = 0; dStrcpy(appWindowTitle, "Emscripten Torque Game Engine"); // directory that contains main.cs . This will help us detect whether we are // running with the scripts in the bundle or not. mainDotCsDir = NULL; useRedirect = true; windowCreated = false; dedicated = false; printf("platstate init\n"); } //------------------------------------------------------------------------------ static void InitWindow(const Point2I &initialSize, const char *name) { gPlatState.windowSize = initialSize; gPlatState.setWindowTitle(name); } #ifndef DEDICATED //------------------------------------------------------------------------------ static bool InitSDL() { if (SDL_Init(SDL_INIT_VIDEO) != 0) return false; atexit(SDL_Quit); SDL_SysWMinfo sysinfo; SDL_VERSION(&sysinfo.version); //if (SDL_GetWMInfo(&sysinfo) == 0) // return false; return true; } //------------------------------------------------------------------------------ static void SetAppState() { U8 state = SDL_GetAppState(); // if we're not active but we have appactive and inputfocus, set window // active and reactivate input if ((gPlatState.backgrounded || !Input::isActive()) && state & SDL_APPACTIVE && state & SDL_APPINPUTFOCUS) { gPlatState.backgrounded = false; Input::reactivate(); Con::printf("Input activated by SetAppState"); } // if we are active, but we don't have appactive or input focus, // deactivate input (if window not locked) and clear windowActive else if (!gPlatState.backgrounded && !(state & SDL_APPACTIVE && state & SDL_APPINPUTFOCUS)) { if (gPlatState.mouseLocked) Input::deactivate(); gPlatState.backgrounded = true; Con::printf("Input deactivated by SetAppState"); } } //------------------------------------------------------------------------------ static S32 NumEventsPending() { static const int MaxEvents = 255; static SDL_Event events[MaxEvents]; SDL_PumpEvents(); #ifdef DUMMY_PLATFORM return 0; #else return SDL_PeepEvents(events, MaxEvents, SDL_PEEKEVENT, 0, SDL_LASTEVENT); #endif } //------------------------------------------------------------------------------ static bool ProcessMessages() { #ifndef DUMMY_PLATFORM SDL_PumpEvents(); // We're going to have to maintain our own list of events here, since the emscripten API // is a bit broken for our purposes... gPlatState.eventList.clear(); gPlatState.eventList.reserve(255); SDL_Event e; while (SDL_PollEvent(&e)) { gPlatState.eventList.push_back(e); } S32 numEvents = gPlatState.eventList.size(); //if (numEvents > 0) // Con::printf("ProcessMessages: %i events pending", numEvents); if (numEvents == 0) return true; for (int i = 0; i < numEvents; ++i) { SDL_Event& event = gPlatState.eventList[i]; //Con::printf("Event.type == %u", event.type); switch (event.type) { case SDL_QUIT: return false; break; case SDL_VIDEORESIZE: case SDL_VIDEOEXPOSE: Game->refreshWindow(); break; case SDL_USEREVENT: Con::printf("User event handled"); if (event.user.code == TORQUE_SETVIDEOMODE) { SetAppState(); // SDL will send a motion event to restore the mouse position // on the new window. Ignore that if the window is locked. if (gPlatState.mouseLocked) { SDL_Event tempEvent; SDL_PeepEvents(&tempEvent, 1, SDL_GETEVENT, SDL_MOUSEMOTION, SDL_MOUSEMOTION); } } break; case SDL_ACTIVEEVENT: Con::printf("Active event"); SetAppState(); break; } } #endif return true; } //------------------------------------------------------------------------------ // send a destroy window event to the window. assumes // window is created. void SendQuitEvent() { SDL_Event quitevent; quitevent.type = SDL_QUIT; SDL_PushEvent(&quitevent); } #endif // DEDICATED //------------------------------------------------------------------------------ static inline void Sleep(int secs, int nanoSecs) { timespec sleeptime; sleeptime.tv_sec = secs; sleeptime.tv_nsec = nanoSecs; nanosleep(&sleeptime, NULL); } #ifndef DEDICATED struct AlertWinState { bool fullScreen; bool cursorHidden; bool inputGrabbed; }; //------------------------------------------------------------------------------ void DisplayErrorAlert(const char* errMsg, bool showSDLError) { char fullErrMsg[2048]; dStrncpy(fullErrMsg, errMsg, sizeof(fullErrMsg)); if (showSDLError) { const char* sdlerror = SDL_GetError(); if (sdlerror != NULL && dStrlen(sdlerror) > 0) { dStrcat(fullErrMsg, " (Error: "); dStrcat(fullErrMsg, sdlerror); dStrcat(fullErrMsg, ")"); } } Platform::AlertOK("Error", fullErrMsg); } //------------------------------------------------------------------------------ static inline void AlertDisableVideo(AlertWinState& state) { state.fullScreen = Video::isFullScreen(); state.cursorHidden = (SDL_ShowCursor(SDL_QUERY) == SDL_DISABLE); state.inputGrabbed = (SDL_WM_GrabInput(SDL_GRAB_QUERY) == SDL_GRAB_ON); if (state.fullScreen) SDL_WM_ToggleFullScreen(SDL_GetVideoSurface()); if (state.cursorHidden) SDL_ShowCursor(SDL_ENABLE); if (state.inputGrabbed) SDL_WM_GrabInput(SDL_GRAB_OFF); } //------------------------------------------------------------------------------ static inline void AlertEnableVideo(AlertWinState& state) { if (state.fullScreen) SDL_WM_ToggleFullScreen(SDL_GetVideoSurface()); if (state.cursorHidden) SDL_ShowCursor(SDL_DISABLE); if (state.inputGrabbed) SDL_WM_GrabInput(SDL_GRAB_ON); } #endif // DEDICATED //------------------------------------------------------------------------------ void Platform::setMouseLock(bool locked) { #ifndef DEDICATED gPlatState.mouseLocked = locked; UInputManager* uInputManager = dynamic_cast( Input::getManager() ); if ( uInputManager && uInputManager->isEnabled() && Input::isActive() ) uInputManager->setWindowLocked(locked); #endif } //------------------------------------------------------------------------------ void Platform::process() { PROFILE_START(XUX_PlatformProcess); if (gPlatState.windowCreated) { #ifndef DEDICATED // process window events PROFILE_START(XUX_ProcessMessages); bool quit = !ProcessMessages(); PROFILE_END(); if(quit) { // generate a quit event Event quitEvent; quitEvent.type = QuitEventType; Game->postEvent(quitEvent); } // process input events PROFILE_START(XUX_InputProcess); Input::process(); PROFILE_END(); // if we're not the foreground window, sleep for 1 ms if (gPlatState.backgrounded) Sleep(0, getBackgroundSleepTime() * 1000000); #endif } else { Sleep(0, getBackgroundSleepTime() * 1000000); } PROFILE_END(); } //------------------------------------------------------------------------------ const Point2I &Platform::getWindowSize() { return gPlatState.windowSize; } //------------------------------------------------------------------------------ void Platform::setWindowSize( U32 newWidth, U32 newHeight ) { gPlatState.windowSize = Point2I(newWidth, newHeight); } //------------------------------------------------------------------------------ void Platform::minimizeWindow() { #ifndef DEDICATED if (gPlatState.windowCreated) SDL_WM_IconifyWindow(); #endif } //------------------------------------------------------------------------------ void Platform::restoreWindow() { } //------------------------------------------------------------------------------ void Platform::shutdown() { Cleanup(); } //------------------------------------------------------------------------------ void Platform::init() { Con::printf("Platform::init"); // Set the platform variable for the scripts Con::setVariable( "$platform", "x86UNIX" ); Con::setVariable( "$platformUnixType", "emscripten" ); EmscriptenConsole::create(); #ifndef DEDICATED Con::printf("Dedicated == %i", gPlatState.dedicated); // if we're not dedicated do more initialization //if (!gPlatState.dedicated) { printf("Init non-dedicated\n"); // init SDL if (!InitSDL()) { Con::printf( " Unable to initialize SDL." ); Platform::AlertOK("Error", "Unable to initialize SDL."); exit(1); } // initialize input Input::init(); Con::printf( "Video Init:" ); // initialize video Video::init(); if ( Video::installDevice( OpenGLDevice::create() ) ) Con::printf( " OpenGL display device detected." ); else Con::printf( " OpenGL display device not detected." ); Con::printf(" "); } #endif } //------------------------------------------------------------------------------ void Platform::initWindow(const Point2I &initialSize, const char *name) { #ifndef DEDICATED // initialize window InitWindow(initialSize, name); if (!InitOpenGL()) { Con::printf( " Unable to init OpenGL window." ); exit(1); } #endif } //------------------------------------------------------------------------------ // Web browser function: //------------------------------------------------------------------------------ bool Platform::openWebBrowser( const char* webAddress ) { return false; } //------------------------------------------------------------------------------ ConsoleFunction( getDesktopResolution, const char*, 1, 1, "getDesktopResolution()" ) { if (!gPlatState.windowCreated) return "0 0 0"; char buffer[256]; char* returnString = Con::getReturnBuffer( dStrlen( buffer ) + 1 ); Resolution res = Video::getDesktopResolution(); dSprintf( buffer, sizeof( buffer ), "%d %d %d", res.w, res.h, res.bpp ); dStrcpy( returnString, buffer ); return( returnString ); } //------------------------------------------------------------------------------ // Silly Korean registry key checker: //------------------------------------------------------------------------------ ConsoleFunction( isKoreanBuild, bool, 1, 1, "isKoreanBuild()" ) { Con::printf("WARNING: isKoreanBuild() is unimplemented"); return false; } //------------------------------------------------------------------------------ void Platform::setWindowTitle( const char* title ) { #ifndef DEDICATED gPlatState.setWindowTitle(title); SDL_WM_SetCaption(gPlatState.appWindowTitle, NULL); #endif } //------------------------------------------------------------------------------ Resolution Video::getDesktopResolution() { Resolution Result; Result.h = _EmscriptenGetDesktopHeight(); Result.w = _EmscriptenGetDesktopWidth(); Result.bpp = _EmscriptenGetDesktopBpp(); return Result; } //----------------------------------------------------------------------------- void Platform::outputDebugString( const char *string ) { fprintf(stderr, "%s", string); fprintf(stderr, "\n" ); fflush(stderr); }