diff --git a/src/libs/tgfclient/guieventloop.cpp b/src/libs/tgfclient/guieventloop.cpp index b02b2bcf9..00fb7a738 100644 --- a/src/libs/tgfclient/guieventloop.cpp +++ b/src/libs/tgfclient/guieventloop.cpp @@ -20,6 +20,36 @@ #include "tgfclient.h" +#include +#include +#include +#include + +//string splitting utils +std::vector &split(const std::string &s, char delim, std::vector &elems) { + std::stringstream ss(s); + std::string item; + while (std::getline(ss, item, delim)) { + elems.push_back(item); + } + return elems; +} + +std::vector split(const std::string &s, char delim) { + std::vector elems; + split(s, delim, elems); + return elems; +} +//to string (from c++11) +template +std::string to_string(T value) +{ + std::ostringstream os ; + os << value ; + return os.str() ; +} + + // Private data (pimp pattern) ============================================= class GfuiEventLoop::Private @@ -293,14 +323,348 @@ void GfuiEventLoop::postRedisplay(void) _pPrivate->bRedisplay = true; } + +///////////////////////////////////////////////// +std::vector msglist; +const int WEBSERVER_IDLE = 0; +const int WEBSERVER_SENDING = 1; +const int WEBSERVER_RECEIVING = 2; +int webserverState = WEBSERVER_IDLE; + +class NotificationManager { + + public: + //constructor + NotificationManager(); + + //destructor + ~NotificationManager(); + + void updateStatus(); + + + private: + void startNewNotification(); + void runAnimation(); + void removeOldUi(); + void createUi(); + void updateWebserverStatusUi(); + + void* screenHandle; + void* prevScreenHandle; + void* menuXMLDescHdle; + int notifyUiIdBg;//the bg image uiid + int notifyUiIdBusyIcon; //the webserver busy icon + std::vector notifyUiId;//the text lines uiid + bool busy; + int textPadding; + std::clock_t animationStartTime; //when the animation started + std::clock_t animationRestStartTime; //when the animation started + std::clock_t animationLastExecTime; //the current time + float totalAnimationDuration;//how much the animation should take to fully run in one direction + float animationRestTime; //how much wes should wait when we a re fully displayed + int animationDirection; + int propertyFinalValue; + std::vector messageLines; + int propertyChangeNeeded; + +}; +NotificationManager::NotificationManager(){ + + this->busy = false; + this->notifyUiIdBg = -1;//the bg image ui id + this->notifyUiIdBusyIcon = -1;//the bg image ui id + + this->animationRestTime = 4 ; //how much wes should wait when we a re fully displayed + this->totalAnimationDuration = 0.3 ;//how much the animation should take to fully run in one direction + this->animationLastExecTime = std::clock(); //the current time + +} +NotificationManager::~NotificationManager(){ + +} +void NotificationManager::updateStatus(){ + + //get the current screen + this->screenHandle = GfuiGetScreen(); + + //get the ui descriptor + this->menuXMLDescHdle = GfuiMenuLoad("notifications.xml"); + + //if we are doing nothing and we have some message to display: let's do it + if(this->busy==false && !msglist.empty()){ + + this->startNewNotification(); + + } + + //if we are running an animation + if(this->busy==true){ + + this->runAnimation(); + + } + + //update webserver status icon + this->updateWebserverStatusUi(); + + //remember the current screen for the next run + this->prevScreenHandle = this->screenHandle; + +} + +void NotificationManager::startNewNotification(){ + + //we are running an animation + this->busy=true; + + //set the animation direction + this->animationDirection=1; + + //retrieve the message to display + std::string newText = msglist.front().c_str(); + + //divide the current message in lines + this->messageLines = split(msglist.front().c_str(), '\n'); + + //reset the start time(s) + this->animationStartTime = this->animationLastExecTime = std::clock(); + this->animationRestStartTime = 0; + + //reset the start property + int propertyCurrentValue=(int)GfParmGetNum(this->menuXMLDescHdle, "dynamic controls/slide", "x", "null", 0); + this->propertyChangeNeeded=(int)GfParmGetNum(this->menuXMLDescHdle, "dynamic controls/slide", "width", "null", 0); + this->propertyFinalValue = propertyCurrentValue + this->propertyChangeNeeded; + + //padding between the text and the bg image + this->textPadding = propertyCurrentValue - (int)GfParmGetNum(this->menuXMLDescHdle, "dynamic controls/slidebg", "x", "null", 0); + + //start + this->animationDirection = 1; + this->runAnimation(); + +} +void NotificationManager::runAnimation(){ + + //read the initial state of the UI + int propertyCurrentValue = (int)GfParmGetNum(this->menuXMLDescHdle, "dynamic controls/slide", "x", "null", 0); + // change needed from current status of the animation to the end + int remainingChangeNeeded = (this->propertyFinalValue - propertyCurrentValue); + + //log current time + std::clock_t currentTime = std::clock(); + + //CASE 1 + //we still need to apply some change to reach the final value + if(remainingChangeNeeded != 0){ + + //how much time is we are running the animation + float animationRunningTime = (currentTime - this->animationStartTime) / (float) CLOCKS_PER_SEC; + + //how much time is passed from the last run + float animationTimeFromLastStep = (currentTime - this->animationLastExecTime) / (float) CLOCKS_PER_SEC; + //time remaining for the animation + float animationTimeRemaining = this->totalAnimationDuration - animationRunningTime; + + // + //int propertyStepChange = remainingChangeNeeded / animationTimeRemaining * animationTimeFromLastStep; + //int propertyStepChange = remainingChangeNeeded / animationTimeRemaining; + //if we have not arhieving 30fps slow down the animation + if(animationTimeFromLastStep > 0,033333333){ + + animationTimeFromLastStep = animationTimeFromLastStep; + + } + int propertyStepChange = this->propertyChangeNeeded / this->totalAnimationDuration * this->animationDirection * animationTimeFromLastStep; + + // if the change is too little we round it up to 1 unit at least + if((propertyStepChange * this->animationDirection) < 1 ){ + + propertyStepChange = 1 * this->animationDirection; + + } + + //new value for the property + int propertyNewValue = propertyCurrentValue + propertyStepChange; + + //it he new value with the change applied is greater that the final result we want we correct it to be equal to the final result + if (propertyNewValue * this->animationDirection > propertyFinalValue * this->animationDirection ){ + + propertyNewValue = propertyFinalValue; + + } + + //apply the new values + GfParmSetNum(this->menuXMLDescHdle, "dynamic controls/slide", "x", "null", propertyNewValue); + GfParmSetNum(this->menuXMLDescHdle, "dynamic controls/slidebg", "x", "null", propertyNewValue - this->textPadding); + + //remember the time we ran the last(this) animation frame + this->animationLastExecTime = currentTime; + + /* + GfLogInfo("###############################\n"); + GfLogInfo("StartTime: %d \n",this->animationStartTime); + GfLogInfo("CurrentTime: %d \n",currentTime); + GfLogInfo("RunningTime: %f \n ",(currentTime - this->animationStartTime) / (float) CLOCKS_PER_SEC); + GfLogInfo("RunningTime: %f \n ",(currentTime - this->animationStartTime)); + GfLogInfo("RunningTime: %f \n ",(float) CLOCKS_PER_SEC); + + GfLogInfo("\n "); + GfLogInfo("AnimationDuration: %f \n ",this->totalAnimationDuration); + GfLogInfo("TimeRemaining: %f \n ",animationTimeRemaining); + GfLogInfo("\n "); + GfLogInfo("FinalValue: %i \n ",this->propertyFinalValue); + GfLogInfo("CurrentValue: %i \n ",propertyCurrentValue); + GfLogInfo("Change Needed: %i \n ",remainingChangeNeeded); + GfLogInfo("StepChange: %i \n ",propertyStepChange); + GfLogInfo("\n "); + GfLogInfo("Direction: %i \n ",this->animationDirection); + */ + + this->removeOldUi(); + this->createUi(); + + } + + + + //CASE 2 + // no change needed while running the runOutAnimation + if(remainingChangeNeeded == 0 && this->animationDirection == -1){ + + //delette this message from the queque + msglist.erase (msglist.begin()); + + //we are no longer busy + this->busy=false; + + } + + + //CASE 3 + // no change needed while running the runInAnimation: we have ended the runInAnimation + if(remainingChangeNeeded == 0 && this->animationDirection == 1){ + if(this->animationRestStartTime==0){ + + //we are just done runnig the runInAnimation + //log the time we start waiting while fully displayed + this->animationRestStartTime = std::clock(); + + }else{ + + //if rest time has expired: start the runOutAnimation + if(((currentTime - this->animationRestStartTime) / (float) CLOCKS_PER_SEC) > this->animationRestTime){ + + //change the animation direction + this->animationDirection = -1; + + //reset the animation start time + this->animationStartTime = this->animationLastExecTime = std::clock(); //when the animation started + + //read property info + this->propertyChangeNeeded= (int)GfParmGetNum(this->menuXMLDescHdle, "dynamic controls/slide", "width", "null", 0); + this->propertyFinalValue = propertyCurrentValue - this->propertyChangeNeeded; + + } + + } + + } + +} + +void NotificationManager::removeOldUi(){ + + //if there is a prev screen + if( GfuiScreenIsActive(this->prevScreenHandle) ){ + + //if there is some prev ui around hide it + if(this->notifyUiIdBg > 0){ + + GfuiVisibilitySet(this->prevScreenHandle, this->notifyUiIdBg, GFUI_INVISIBLE); + + } + + //iterate trougth ui and set them invisible + for (int i; i < notifyUiId.size(); i++) { + + GfuiVisibilitySet(this->prevScreenHandle, this->notifyUiId[i], GFUI_INVISIBLE); + + } + + } + + //delete the prev ui's + this->notifyUiId.clear(); + this->notifyUiIdBg=-1; +} + +void NotificationManager::createUi(){ + + //create the new UI + this->notifyUiIdBg = GfuiMenuCreateStaticImageControl(this->screenHandle, this->menuXMLDescHdle, "slidebg"); + GfuiVisibilitySet(this->screenHandle, this->notifyUiIdBg, GFUI_VISIBLE); + + //get first line vertical position + int ypos=(int)GfParmGetNum(this->menuXMLDescHdle, "dynamic controls/slide", "y", "null", 0); + int yposmod= ypos; + + //iterate trougth lines + for (int i; i < this->messageLines.size(); i++) { + + int uiId; + uiId= GfuiMenuCreateLabelControl(this->screenHandle, this->menuXMLDescHdle, "slide"); + + //change the vertical position + int yposmod = ypos - (i+1)*(10); + GfParmSetNum(this->menuXMLDescHdle, "dynamic controls/slide", "y", "null", yposmod); + + GfuiLabelSetText(this->screenHandle, uiId, this->messageLines[i].c_str()); + GfuiVisibilitySet(this->screenHandle, uiId, GFUI_VISIBLE); + this->notifyUiId.push_back(uiId); + + } + + //reset ypos + GfParmSetNum(this->menuXMLDescHdle, "dynamic controls/slide", "y", "null", ypos); +} +void NotificationManager::updateWebserverStatusUi(){ + + //if there is some prev ui around hide it + if(this->notifyUiIdBusyIcon > 0){ + + GfuiVisibilitySet(this->prevScreenHandle, this->notifyUiIdBusyIcon, GFUI_INVISIBLE); + + } + + if(this->screenHandle > 0){ + //if webserver is busy display busy icon + std::string webServerIcon = "busyicon"; + webServerIcon.append(to_string(webserverState)); + + this->notifyUiIdBusyIcon = GfuiMenuCreateStaticImageControl(this->screenHandle, this->menuXMLDescHdle, webServerIcon.c_str()); + GfuiVisibilitySet(this->screenHandle, this->notifyUiIdBusyIcon, GFUI_VISIBLE); + + } + +} + +NotificationManager notifications; + void GfuiEventLoop::forceRedisplay() { + notifications.updateStatus(); + if (_pPrivate->cbDisplay) _pPrivate->cbDisplay(); } + void GfuiEventLoop::redisplay() { + //temp + _pPrivate->bRedisplay=true; + // Refresh display if requested and if any redisplay CB. if (_pPrivate->bRedisplay) { diff --git a/src/modules/racing/standardgame/raceinit.cpp b/src/modules/racing/standardgame/raceinit.cpp index 5cfc072f8..4d89c0322 100644 --- a/src/modules/racing/standardgame/raceinit.cpp +++ b/src/modules/racing/standardgame/raceinit.cpp @@ -27,6 +27,8 @@ #include #include #include +#include +#include #ifdef THIRD_PARTY_SQLITE3 #include @@ -59,6 +61,7 @@ #include #include #include +#include //#include #include #include @@ -84,6 +87,8 @@ std::string to_string(T value) } CURLM* multi_handle; +extern std::vector msglist; +extern int webserverState; /* START webserver*/ WebServer::WebServer(){ @@ -111,11 +116,18 @@ WebServer::~WebServer(){ curl_multi_cleanup(multi_handle); } int WebServer::updateAsyncStatus(){ + //perform the pending requests curl_multi_perform(multi_handle, &this->handle_count); + if( this->handle_count>0){ GfLogInfo("############################# ASYNC WAITING UPDATES: %i\n", this->handle_count); + //display some UI to the user to inform him we are waiting a reply from the server +webserverState=2; + }else{ +webserverState=0; } + CURLMsg *msg; CURL *eh=NULL; CURLcode return_code; @@ -129,6 +141,10 @@ int WebServer::updateAsyncStatus(){ return_code = msg->data.result; if(return_code!=CURLE_OK) { fprintf(stderr, "CURL error code: %d\n", msg->data.result); + + //something went wrong. anyway we are no more busy + webserverState=0; + continue; } @@ -144,24 +160,42 @@ int WebServer::updateAsyncStatus(){ if(http_status_code==200) { printf("200 OK for %s\n", szUrl); - GfLogInfo("############################# ASYNC SERVER REPLY:\n %s\n", this->curlServerReply.c_str()); - //manage server replyes + GfLogInfo("############################# ASYNC SERVER REPLY:\n%s\n", this->curlServerReply.c_str()); + //manage server replyes... + //read the xml reply of the server void *xmlReply; xmlReply = GfParmReadBuf(const_cast(this->curlServerReply.c_str())); - //login reply - //store the webServer session and id assigned - if(GfParmGetNum(xmlReply, "content/reply/login", "id", "null",0) != 0){ - this->sessionId = GfParmGetStr(xmlReply, "content/reply/login", "sessionid", "null"); - this->userId = GfParmGetNum(xmlReply, "content/reply/login", "id", "null",0); + //the server want we display something? + //todo: if more than one messagge only the last one is show to the user... +//GfParmListSeekFirst +//GfParmListSeekNext + if(GfParmExistsSection(xmlReply, "content/reply/messages")){ + int msgsCount = GfParmGetNum(xmlReply, "content/reply/messages", "number", "null",0); + if( msgsCount > 0 ){ + for( int dispatchedMsgs = 0; dispatchedMsgs < msgsCount; dispatchedMsgs = dispatchedMsgs + 1 ) + { + std::string msgTag = "message"; + msgTag.append(to_string(dispatchedMsgs)); + GfLogInfo("n\%s\n", msgTag.c_str()); + + //ReSituation::self().setRaceMessage(GfParmGetStr(xmlReply, "content/reply/messages", msgTag.c_str(), "null"),5, false); + msglist.push_back(GfParmGetStr(xmlReply, "content/reply/messages", msgTag.c_str(), "null")); + } + } } + //race reply //store the webServer assigned race id - if(GfParmGetNum(xmlReply, "content/reply/races", "id", "null", 0) != 0){ - this->raceId = (int)GfParmGetNum(xmlReply, "content/reply/races", "id", "null", 0); - GfLogInfo("WebServer - assigned race id is: %i\n", this->raceId); + if(GfParmExistsSection(xmlReply, "content/reply/races")){ + if(GfParmGetNum(xmlReply, "content/reply/races", "id", "null", 0) != 0){ + this->raceId = (int)GfParmGetNum(xmlReply, "content/reply/races", "id", "null", 0); + GfLogInfo("WebServer - assigned race id is: %i\n", this->raceId); + msglist.push_back("Webserver assigned a raceid"); + } } + //empty the string this->curlServerReply.clear(); } else { @@ -179,6 +213,7 @@ int WebServer::updateAsyncStatus(){ return 0; } + int WebServer::addAsyncRequest(std::string const data){ GfLogInfo("############################# ADD ASYNC REQUEST:\n %s \n", data.c_str()); @@ -218,6 +253,9 @@ int WebServer::addAsyncRequest(std::string const data){ //add the request to the queque curl_multi_add_handle(multi_handle, curl); + //pending request + //webserverBusy=true; +webserverState=1; return 0; } @@ -226,7 +264,7 @@ int WebServer::sendGenericRequest (std::string data, std::string& serverReply){ CURLcode res; GfLogInfo("WebServer - SENDING data to server:\n %s \n", data.c_str()); - +webserverState=1; //insert "data=" before the actual data data.insert(0,"data="); const char *postthis=data.c_str(); @@ -246,7 +284,7 @@ int WebServer::sendGenericRequest (std::string data, std::string& serverReply){ // some servers don't like requests that are made without a user-agent // field, so we provide one curl_easy_setopt(curl, CURLOPT_USERAGENT, "libcurl-agent/1.0"); - + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postthis); // if we don't provide POSTFIELDSIZE, libcurl will strlen() by @@ -258,6 +296,7 @@ int WebServer::sendGenericRequest (std::string data, std::string& serverReply){ // Check for errors if(res != CURLE_OK) { + msglist.push_back("WebServer: failed to connect!"); fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res)); } @@ -270,7 +309,8 @@ int WebServer::sendGenericRequest (std::string data, std::string& serverReply){ // GfLogInfo("WebServer - RECEIVING data from server:\n %s\n", this->curlServerReply.c_str()); - +webserverState=2; + serverReply = this->curlServerReply; //empty the string this->curlServerReply.clear(); } @@ -282,10 +322,8 @@ int WebServer::sendGenericRequest (std::string data, std::string& serverReply){ curl_global_cleanup(); } return 0; - } - int WebServer::readUserConfig (int userId){ void *prHandle; char prefFileUrl[256]; @@ -313,6 +351,15 @@ int WebServer::sendLogin (int userId){ //read username and password and save it in as webserver properties this->readUserConfig(userId); + + std::string username="username"; + std::string password="password"; + + //if the user has not setup the webserver login info abort the login + if(username==this->username && password==this->password){ + GfLogInfo("WebServer - send of login info aborted (the user is not correctly setup).\n"); + return 1; + } //prepare the string to send std::string dataToSend (""); @@ -334,13 +381,32 @@ int WebServer::sendLogin (int userId){ this->sendGenericRequest(dataToSend, serverReply); + GfLogInfo("WebServer - PROCESSING SERVER REPLY:\n%s\n", serverReply.c_str()); + //read the xml reply of the server void *xmlReply; xmlReply = GfParmReadBuf(const_cast(serverReply.c_str())); - //store the webServer session and id assigned - this->sessionId = GfParmGetStr(xmlReply, "content/reply/login", "sessionid", "null"); - this->userId = GfParmGetNum(xmlReply, "content/reply/login", "id", "null",0); + //login reply + //store the webServer session and id assigned if available + if(GfParmExistsSection(xmlReply, "content/reply/login")){ + if(GfParmGetNum(xmlReply, "content/reply/login", "id", "null",0) != 0){ + GfLogInfo("WebServer - loggin succeded.\n"); + msglist.push_back("WebServer: LOGGED IN!"); + //store the webServer session and id assigned + this->sessionId = GfParmGetStr(xmlReply, "content/reply/login", "sessionid", "null"); + this->userId = GfParmGetNum(xmlReply, "content/reply/login", "id", "null",0); + }else{ + GfLogInfo("WebServer - Login Failed: probably wrong username or password.\n"); + msglist.push_back("WebServer: Login Failed:\nwrong username or password."); + return 1; + } + }else{ + GfLogInfo("WebServer - Login Failed: bad reply from the server.\n"); + msglist.push_back("WebServer: Login Failed:\nbad reply from the server."); + return 1; + } + GfLogInfo("WebServer - assigned session id is: %s\n", this->sessionId); @@ -348,6 +414,12 @@ int WebServer::sendLogin (int userId){ } int WebServer::sendLap (int race_id, double laptime, double fuel, int position, int wettness){ + //are we logged in? + if(this->sessionId=='\0'){ + GfLogInfo("WebServer - send of lap info aborted. No session ID assigned (we are not logged-in).\n"); + return 1; + } + //prepare the string to send std::string dataToSend (""); dataToSend.append( "" @@ -381,6 +453,12 @@ int WebServer::sendRaceStart (int user_skill, const char *track_id, char *car_id std::string mysetup; std::string dataToSend; + //are we logged in? + if(this->sessionId=='\0'){ + GfLogInfo("WebServer - send of racestart info aborted. No session ID assigned (we are not logged in)"); + return 1; + } + //read the setup GfParmWriteString(setup, mysetup); @@ -420,6 +498,12 @@ int WebServer::sendRaceStart (int user_skill, const char *track_id, char *car_id int WebServer::sendRaceEnd (int race_id, int endposition){ std::string serverReply; + //are we logged in? + if(this->sessionId=='\0'){ + GfLogInfo("WebServer - send of raceend info aborted. No session ID assigned (we are not logged in)"); + return 1; + } + //prepare the string to send std::string dataToSend (""); dataToSend.append( "" diff --git a/src/modules/racing/standardgame/raceinit.h b/src/modules/racing/standardgame/raceinit.h index 0e70f8226..5cf9b69e1 100644 --- a/src/modules/racing/standardgame/raceinit.h +++ b/src/modules/racing/standardgame/raceinit.h @@ -83,7 +83,7 @@ WriteMemoryCallback(void *contents, size_t size, size_t nmemb, void *userp) return realsize; } -* */ +*/ static size_t WriteStringCallback(void *contents, size_t size, size_t nmemb, void *userp) { ((std::string*)userp)->append((char*)contents, size * nmemb); @@ -137,6 +137,7 @@ class WebServer { * line 671 * */ + #endif /* _RACEINIT_H_ */ diff --git a/src/modules/userinterface/legacymenu/racescreens/raceloadingmenu.cpp b/src/modules/userinterface/legacymenu/racescreens/raceloadingmenu.cpp index a7e7eda5d..b96ba8173 100644 --- a/src/modules/userinterface/legacymenu/racescreens/raceloadingmenu.cpp +++ b/src/modules/userinterface/legacymenu/racescreens/raceloadingmenu.cpp @@ -115,6 +115,8 @@ RmLoadingScreenStart(const char *title, const char *bgimg) // Display screen. GfuiScreenActivate(HScreen); GfuiDisplay(); + //force one redisplay + GfuiApp().eventLoop().forceRedisplay(); } void @@ -174,5 +176,7 @@ RmLoadingScreenSetText(const char *text) while (i != CurTextLineIdx); GfuiDisplay(); + //force one redisplay + GfuiApp().eventLoop().forceRedisplay(); }