diff --git a/src/app/ScreenManager.cpp b/src/app/ScreenManager.cpp index 03e581b..1a67b63 100644 --- a/src/app/ScreenManager.cpp +++ b/src/app/ScreenManager.cpp @@ -1,6 +1,8 @@ #include "ScreenManager.h" -ScreenManager::ScreenManager(Adafruit_SSD1306 &display, SDCardManager *sdCardManager) : _displayRef(display), _displayColorInverted(false), _displayDimmed(false), _enabled(true), _currentView(NO_CURRENT_VIEW), _error(OK) +ScreenManager::ScreenManager(Adafruit_SSD1306 &display, SDCardManager *sdCardManager) : _displayRef(display), _error(OK) +, _displayColorInverted(false), _displayDimmed(false), _enabled(true), _refreshRateHz(1), _refreshInterval(1000), _timeRef(0),_forceRefresh(false) +, _currentView(NO_CURRENT_VIEW), _tail(NULL) , _viewNotFound{&(displayError), (ErrorInfo *)malloc(sizeof(ErrorInfo)), RESERVED_VIEW_UID, NULL} , _viewFuncUndefined{&(displayError), (ErrorInfo *)malloc(sizeof(ErrorInfo)), RESERVED_VIEW_UID, NULL} , _currentViewUndefined{&(displayError), (ErrorInfo *)malloc(sizeof(ErrorInfo)), RESERVED_VIEW_UID, NULL} @@ -8,6 +10,10 @@ ScreenManager::ScreenManager(Adafruit_SSD1306 &display, SDCardManager *sdCardMan ,_sdCardManager(sdCardManager) { _viewLinkedList = (ViewLinkedList) createEmptyList(); + //We set the error messages : + ((ErrorInfo *)_viewNotFound.pData)->errorMessage = FPSTR("View does not exist"); + ((ErrorInfo *)_viewFunctionFailedToExecute.pData)->errorMessage = FPSTR("View function failed\nto execute"); + ((ErrorInfo *)_viewFuncUndefined.pData)->errorMessage = FPSTR("View function is NULL"); } boolean ScreenManager::init() @@ -66,8 +72,8 @@ void ScreenManager::setDefault() boolean ScreenManager::addView(boolean (*viewLogicFunction)(Adafruit_SSD1306&, void*), void *pData, const unsigned char UID) { - ViewLink viewLink ={viewLogicFunction, pData, UID, NULL}; - addNewLinkAtTheEnd(&_viewLinkedList, viewLink); + ViewLink viewLink ={viewLogicFunction, pData, UID, NULL, NULL}; + return addNewLinkAtTheEnd(&_viewLinkedList, viewLink); } void *ScreenManager::createEmptyList() @@ -87,33 +93,48 @@ boolean ScreenManager::addNewLinkAtTheEnd(ViewLinkedList *viewLinkedList, ViewLi //Because of the const member memcpy(newViewLink, &viewLink, sizeof(*newViewLink)); - if(isListEmpty(*viewLinkedList))*viewLinkedList = newViewLink; - else + //If this is the first link + if(isListEmpty(*viewLinkedList)) { - if((*viewLinkedList)->UID == newViewLink->UID) + //The link list points to the first link (aka the head) + *viewLinkedList = newViewLink; + //The previous pointer is still NULL; + //The _tail pointer is the same as the head one. + _tail = *viewLinkedList; + } + else //If there are some links already + { + ViewLinkedList *cursor = viewLinkedList; + ViewLink *previousLink = NULL; + + while(*cursor != NULL) { - ViewLink *link = *viewLinkedList; - *viewLinkedList = newViewLink; - newViewLink->next = link->next; - free(link); - return true; - } - - ViewLinkedList cursor = *viewLinkedList; - - while(cursor->next != NULL) - { - if(cursor->next->UID == newViewLink->UID) + previousLink = *cursor; + //We check if the UID already exists, if yes we replace the link + if((*cursor)->UID == newViewLink->UID) { - ViewLink *link = cursor->next; - cursor->next = newViewLink; - newViewLink->next = link->next; - free(link); + ViewLink *toDelete = *cursor; + *cursor = newViewLink; + newViewLink->next = toDelete->next; + newViewLink->previous = toDelete->previous; + //We check if the link as a next link registered + if(toDelete->next != NULL) + { + //We update it's previous link since it's going to be deleted + toDelete->next->previous = newViewLink; + } + //We do not forget to check if the link replaced is also the tail, in this case, we must update the tail + if(_tail == toDelete)_tail = newViewLink; + free(toDelete); + return true; - } - cursor = cursor->next; + } + cursor = &(*cursor)->next; } - cursor->next = newViewLink; + newViewLink->previous = previousLink; + *cursor = newViewLink; + //We need to update the tail + _tail = newViewLink; } return true; @@ -128,25 +149,28 @@ boolean ScreenManager::removeLinkByUID(ViewLinkedList *viewLinkedList, const uns { if(isListEmpty(*viewLinkedList))return false; - if((*viewLinkedList)->UID == UID) - { - ViewLink *tmp = *viewLinkedList; - *viewLinkedList = (*viewLinkedList)->next; - free(tmp); - return true; - } + ViewLinkedList *cursor = viewLinkedList; - ViewLinkedList cursor = *viewLinkedList; - while(!isListEmpty(cursor->next)) + while(*cursor != NULL) { - if(cursor->next->UID == UID) + //We check if this is the link we want to delete + if((*cursor)->UID == UID) { - ViewLink *tmp = cursor->next; - cursor->next = cursor->next->next; - free(tmp); + ViewLink *toDelete = *cursor; + *cursor = (*cursor)->next; + + if(toDelete->next != NULL) + { + //We update the previous link of the next member since it's going to be deleted + toDelete->next->previous = toDelete->previous; + } + //We also update the tail if the link we delete was the tail + if(_tail == toDelete)_tail = toDelete->previous; + + free(toDelete); return true; - } - cursor = cursor->next; + } + cursor = &(*cursor)->next; } return false; } @@ -186,119 +210,135 @@ ViewLink* ScreenManager::getLinkByUID(ViewLinkedList viewLinkedList, const unsig return NULL; } -boolean ScreenManager::displayView(const int UID) +void ScreenManager::run() +{ + if((millis() - _timeRef < _refreshInterval && !_forceRefresh) || !_enabled) return; + + _timeRef = millis(); + + if(((_error == OK || _error == VIEW_FAILED_TO_EXECUTE) || _forceRefresh) && _currentView != NO_CURRENT_VIEW) + { + _displayRef.clearDisplay(); + _displayRef.setCursor(0,0); + _displayRef.setTextSize(1); + + switch(_error) + { + case OK: + case VIEW_FAILED_TO_EXECUTE: + if(!(*_currentView->viewLogicFunction)(_displayRef, _currentView->pData)) + { + _displayRef.clearDisplay(); + //Error while executing the view function + ((ErrorInfo *)_viewFunctionFailedToExecute.pData)->viewUID = _currentView->UID; + (*_viewFunctionFailedToExecute.viewLogicFunction)(_displayRef, _viewFunctionFailedToExecute.pData); + + _error = VIEW_FAILED_TO_EXECUTE; + } + else + _error = OK; + break; + case VIEW_NOT_FOUND: + (*_viewNotFound.viewLogicFunction)(_displayRef, _viewNotFound.pData); + break; + case VIEW_FUNC_UNDEFINED: + (*_viewFuncUndefined.viewLogicFunction)(_displayRef, _viewFuncUndefined.pData); + break; + } + + _displayRef.display(); + _forceRefresh = false; + } +} + +boolean ScreenManager::displayView(const uint8_t UID) { if(!_enabled) return true; - //Reset draw settings: - _displayRef.clearDisplay(); - _displayRef.setCursor(0,0); - _displayRef.setTextSize(1); - - - if(UID == -1 && _currentView == NO_CURRENT_VIEW) - { - //We display an error message on the screen - ((ErrorInfo *)_currentViewUndefined.pData)->errorMessage = FPSTR("Could not display current view"); - ((ErrorInfo *)_currentViewUndefined.pData)->viewUID = UID; - (*_currentViewUndefined.viewLogicFunction)(_displayRef, _currentViewUndefined.pData); - _displayRef.display(); - - _error = CURRENT_VIEW_UNDEFINED; - return false; - } - else if(UID == -1) - { - if(_currentView->viewLogicFunction == NULL) - { - //We display an error message on the screen - ((ErrorInfo *)_viewFuncUndefined.pData)->errorMessage = FPSTR("View function is NULL"); - ((ErrorInfo *)_viewFuncUndefined.pData)->viewUID = _currentView->UID; - (*_viewFuncUndefined.viewLogicFunction)(_displayRef, _viewFuncUndefined.pData); - _displayRef.display(); - - _error = VIEW_FUNC_UNDEFINED; - return false; - } - else if(!(*_currentView->viewLogicFunction)(_displayRef, _currentView->pData)) - { - //We display an error message on the screen - _displayRef.clearDisplay(); - ((ErrorInfo *)_viewFunctionFailedToExecute.pData)->errorMessage = FPSTR("View function failed\nto execute"); - ((ErrorInfo *)_viewFunctionFailedToExecute.pData)->viewUID = _currentView->UID; - (*_viewFunctionFailedToExecute.viewLogicFunction)(_displayRef, _viewFunctionFailedToExecute.pData); - _displayRef.display(); - - _error = VIEW_FAILED_TO_EXECUTE; - return false; - } - - _error = OK; - _displayRef.display(); - return true; - } - + _forceRefresh = true; ViewLink *viewLink = getLinkByUID(_viewLinkedList, UID); + if(viewLink == NULL) { //We display an error message on the screen - ((ErrorInfo *)_viewNotFound.pData)->errorMessage = FPSTR("View does not exist"); ((ErrorInfo *)_viewNotFound.pData)->viewUID = UID; - (*_viewNotFound.viewLogicFunction)(_displayRef, _viewNotFound.pData); - _displayRef.display(); - _error = VIEW_NOT_FOUND; - _currentView = &_viewNotFound; return false; - }else if(viewLink->viewLogicFunction == NULL) + } + else if(viewLink->viewLogicFunction == NULL) { //We display an error message on the screen - ((ErrorInfo *)_viewFuncUndefined.pData)->errorMessage = FPSTR("View function is NULL"); ((ErrorInfo *)_viewFuncUndefined.pData)->viewUID = UID; - (*_viewFuncUndefined.viewLogicFunction)(_displayRef, _viewFuncUndefined.pData); - _displayRef.display(); - _error = VIEW_FUNC_UNDEFINED; _currentView = viewLink; return false; } - - - if(!(*viewLink->viewLogicFunction)(_displayRef, viewLink->pData)) - { - //We display an error message on the screen - _displayRef.clearDisplay(); - ((ErrorInfo *)_viewFunctionFailedToExecute.pData)->errorMessage = FPSTR("View function failed\nto execute"); - ((ErrorInfo *)_viewFunctionFailedToExecute.pData)->viewUID = UID; - (*_viewFunctionFailedToExecute.viewLogicFunction)(_displayRef, _viewFunctionFailedToExecute.pData); - _displayRef.display(); - _error = VIEW_FAILED_TO_EXECUTE; - _currentView = viewLink; - return false; - } - - _displayRef.display(); _currentView = viewLink; _error = OK; - return true; + run(); + return _error == OK; } -boolean ScreenManager::displayNextView() +void ScreenManager::displayNextView() { + _forceRefresh = true; + _error = OK; if(_currentView == NO_CURRENT_VIEW && !isListEmpty(_viewLinkedList)) { _currentView = _viewLinkedList; - return displayView(); } - - if(!isListEmpty(_currentView->next)) + else if(!isListEmpty(_currentView->next)) { _currentView = _currentView->next; - return displayView(); + } + //End of the views, we cycle again :) + else if(isListEmpty(_currentView->next) && !isListEmpty(_viewLinkedList)) + { + _currentView = _viewLinkedList; } - _currentView = _viewLinkedList; - return displayView(); + //We check if the view function is defined : + if(_currentView != NO_CURRENT_VIEW) + { + if(_currentView->viewLogicFunction == NULL) + { + ((ErrorInfo *)_viewFuncUndefined.pData)->viewUID = _currentView->UID; + _error = VIEW_FUNC_UNDEFINED; + } + } + + run(); +} + +void ScreenManager::displayPreviousView() +{ + _forceRefresh = true; + _error = OK; + if(_currentView == NO_CURRENT_VIEW && !isListEmpty(_tail)) + { + _currentView = _tail; + } + else if(!isListEmpty(_currentView->previous)) + { + _currentView = _currentView->previous; + } + //End of the views, we cycle again :) + else if(isListEmpty(_currentView->previous) && !isListEmpty(_tail)) + { + _currentView = _tail; + } + + //We check if the view function is defined : + if(_currentView != NO_CURRENT_VIEW) + { + if(_currentView->viewLogicFunction == NULL) + { + ((ErrorInfo *)_viewFuncUndefined.pData)->viewUID = _currentView->UID; + _error = VIEW_FUNC_UNDEFINED; + } + } + + run(); } void ScreenManager::invertDisplayColor(const boolean inverted) @@ -354,6 +394,20 @@ void ScreenManager::clearDisplay(const boolean bufferOnly) } } +void ScreenManager::clearViews() +{ + ViewLinkedList cursor = _viewLinkedList; + while(!isListEmpty(cursor)) + { + ViewLink *toDelete = cursor; + cursor = cursor->next; + free(cursor); + } + //Do not forget to mark the list as empty + _viewLinkedList = NULL; + _tail = NULL; +} + void ScreenManager::setEnabled(boolean value) { _enabled = value; diff --git a/src/app/ScreenManager.h b/src/app/ScreenManager.h index cf815d1..40d3a2e 100644 --- a/src/app/ScreenManager.h +++ b/src/app/ScreenManager.h @@ -16,18 +16,21 @@ class ScreenManager boolean addView(boolean (*viewLogicFunction)(Adafruit_SSD1306&, void*), void *pData, const unsigned char UID); boolean removeView(const unsigned char UID); - boolean displayView(const int UID = -1); - boolean displayNextView(); + boolean displayView(const uint8_t UID); + void displayNextView(); + void displayPreviousView(); boolean applyCfgFromSD(); void invertDisplayColor(const boolean inverted); void orientDisplay(const Orientation orientation); void dimDisplay(const boolean dimmed); void clearDisplay(const boolean bufferOnly = false); + void clearViews(); void sleep(); void wakeUp(); void setEnabled(boolean value); boolean getEnabled(); boolean init(); + void run(); Error getError() const; const char* getErrorMessage() const; @@ -58,7 +61,11 @@ class ScreenManager boolean _displayColorInverted; boolean _displayDimmed; boolean _enabled; - ViewLink* _currentView; + uint8_t _refreshRateHz; + uint16_t _refreshInterval; + uint64_t _timeRef; + boolean _forceRefresh; + ViewLink *_currentView, *_tail; ViewLink _viewNotFound, _viewFuncUndefined, _currentViewUndefined, _viewFunctionFailedToExecute; SDCardManager *_sdCardManager;