- add webserver status Icoons

- add notification background
- add xml notification UI definition
- implement notification class
- use it were possible
- forse screen redisplay to use the new animations

git-svn-id: https://svn.code.sf.net/p/speed-dreams/code/trunk@6261 30fe4595-0a0c-4342-8851-515496e4dcbd

Former-commit-id: fa76125a24bfd25987a22a40ab3b430e3d64b851
Former-commit-id: 0669043928b9d2db165617b49e880bb9c366ff20
This commit is contained in:
madbad 2015-11-23 19:43:33 +00:00
parent 8223cded18
commit 34861b5dc1
4 changed files with 472 additions and 19 deletions

View file

@ -20,6 +20,36 @@
#include "tgfclient.h"
#include <vector>
#include <string>
#include <sstream>
#include <ctime>
//string splitting utils
std::vector<std::string> &split(const std::string &s, char delim, std::vector<std::string> &elems) {
std::stringstream ss(s);
std::string item;
while (std::getline(ss, item, delim)) {
elems.push_back(item);
}
return elems;
}
std::vector<std::string> split(const std::string &s, char delim) {
std::vector<std::string> elems;
split(s, delim, elems);
return elems;
}
//to string (from c++11)
template <typename T>
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<std::string> 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<int> 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<std::string> 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)
{

View file

@ -27,6 +27,8 @@
#include <string>
#include <sstream>
#include <map>
#include <ctime>
#include <iostream>
#ifdef THIRD_PARTY_SQLITE3
#include <sqlite3.h>
@ -59,6 +61,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sstream>
//#include <curl/curl.h>
#include <curl/multi.h>
#include <playerpref.h>
@ -84,6 +87,8 @@ std::string to_string(T value)
}
CURLM* multi_handle;
extern std::vector<std::string> 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<char*>(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<char*>(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( "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
@ -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( "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"

View file

@ -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_ */

View file

@ -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();
}