/* * ServiceGUI.cpp * * Created on: 22 maj 2019 * Author: robert */ #include //module-os #include "FreeRTOS.h" #include "semphr.h" //module-gui #include "gui/core/Font.hpp" #include "gui/core/Context.hpp" #include "gui/input/Translator.hpp" //gui service #include "messages/GUIMessage.hpp" #include "messages/DrawMessage.hpp" //service-eink #include "service-eink/messages/ImageMessage.hpp" #include "ServiceGUI.hpp" #include "service-appmgr/ApplicationManager.hpp" #include "../gui/core/ImageManager.hpp" #include "log/log.hpp" extern "C" #include "memory/usermem.h" #include "SystemManager/SystemManager.hpp" #include "WorkerGUI.hpp" namespace sgui { static uint32_t getTimeFunction() { return xTaskGetTickCount(); } ServiceGUI::ServiceGUI(const std::string& name, std::string parent, uint32_t screenWidth, uint32_t screenHeight) : sys::Service(name, parent, 4096, sys::ServicePriority::Idle), renderContext{ nullptr }, transferContext { nullptr }, renderFrameCounter{ 1 }, transferedFrameCounter{ 0 }, screenWidth{ screenWidth }, screenHeight{ screenHeight }, semCommands{ NULL }, worker{nullptr} { LOG_INFO("[ServiceGUI] Initializing"); //allocate buffers for rendering and transferring data to eink renderContext = new gui::Context( screenWidth, screenHeight ); transferContext = new gui::Context( screenWidth, screenHeight ); //load fonts gui::FontManager& fontManager = gui::FontManager::getInstance(); fontManager.init( "sys/assets" ); //load images gui::ImageManager& imageManager = gui::ImageManager::getInstance(); imageManager.init( "sys/assets" ); //load keyboard profiles translator.init("sys/assets"); } ServiceGUI::~ServiceGUI(){ LOG_INFO("[ServiceGUI] Cleaning resources"); if( renderContext ) delete renderContext; if( transferContext ) delete transferContext; worker->deinit(); gui::ImageManager::getInstance().clear(); gui::FontManager::getInstance().clear(); } void ServiceGUI::sendBuffer() { //copy data from render context to transfer context transferContext->insert( 0, 0, renderContext ); auto msg = std::make_shared( 0, 0, transferContext->getW(), transferContext->getH(), (mode==gui::RefreshModes::GUI_REFRESH_DEEP?true:false), transferContext->getData(), suspendInProgress, shutdownInProgress ); einkReady = false; auto ret = sys::Bus::SendUnicast(msg, "ServiceEink", this,2000); if( ret.first == sys::ReturnCodes::Success ) { transferedFrameCounter = renderFrameCounter; } //set default refreshing mode. mode = gui::RefreshModes::GUI_REFRESH_FAST; } void ServiceGUI::sendToRender() { rendering = true; worker->send(static_cast(WorkerGUICommands::Render), NULL); } // Invoked upon receiving data message sys::Message_t ServiceGUI::DataReceivedHandler(sys::DataMessage* msgl,sys::ResponseMessage* resp) { sgui::GUIMessage* msg = static_cast(msgl); switch( msg->messageType ) { case static_cast(MessageType::MessageTypeUninitialized): { // LOG_ERROR("[ServiceGUI] Received uninitialized message type"); } break; case static_cast( MessageType::GUICommands ): { // LOG_INFO("[%s] GUICommands", GetName().c_str()); auto dmsg = static_cast( msgl ); if( !dmsg->commands.empty() ) { //if suspend flag is set ignore any new message if( !suspendInProgress ) { if( dmsg->command == sgui::DrawMessage::DrawCommand::SHUTDOWN ) { LOG_WARN( "Shutdown - received shutdown draw commands" ); shutdownInProgress = true; } //if message carries suspend flag set flag in service and proceed if( dmsg->command == sgui::DrawMessage::DrawCommand::SUSPEND ) { LOG_WARN( "Suspended - received suspend draw commands" ); suspendInProgress = true; } //update mode if( dmsg->mode == gui::RefreshModes::GUI_REFRESH_DEEP ) { mode = dmsg->mode; } // LOG_INFO("[ServiceGUI] Received %d draw commands", dmsg->commands.size()); //lock access to commands vector, clear it and then copy commands from message to vector if( xSemaphoreTake( semCommands, pdMS_TO_TICKS(1000) ) == pdTRUE ) { commands.clear(); for (auto it = dmsg->commands.begin(); it != dmsg->commands.end(); it++) commands.push_back(std::move(*it)); xSemaphoreGive( semCommands ); } else { LOG_ERROR("Failed to acquire semaphore"); } //if worker is not rendering send him new set of commands if( !rendering ) { sendToRender(); } else { // LOG_ERROR("Already rendering"); } // uint32_t mem = usermemGetFreeHeapSize(); // LOG_WARN( "Heap Memory: %d", mem ); } else { LOG_WARN( "Suspended - ignoring draw commands" ); } } } break; case static_cast(MessageType::GUIRenderingFinished): { // LOG_INFO("[%s] GUIRenderingFinished", GetName().c_str()); //increment counter holding number of drawn frames rendering = false; renderFrameCounter++; //copy render buffer to transfer buffer using semaphore to protect data //gui service is locking semaphore, makes a copy and then sends message to eink if( einkReady ) { sendBuffer(); } else if( !requestSent ){ requestSent = true; //request eink state auto msg = std::make_shared(MessageType::EinkStateRequest); sys::Bus::SendUnicast(msg, "ServiceEink", this); } }break; case static_cast( MessageType::GUIFocusInfo ): { // LOG_INFO("[%s] GUIFocusInfo", GetName().c_str()); } break; case static_cast( MessageType::GUIDisplayReady ): { // LOG_INFO("[%s] GUIDisplayReady", GetName().c_str()); einkReady = true; requestSent = false; if( msg->getShutdown()){ einkReady = false; suspendInProgress = false; LOG_DEBUG("last rendering before shutdown finished."); sys::SystemManager::CloseSystem(this); } if( msg->getSuspend()){ einkReady = false; suspendInProgress = false; LOG_DEBUG("last rendering before suspend is finished."); sapm::ApplicationManager::messageInitPowerSaveMode(this); } //mode = gui::RefreshModes::GUI_REFRESH_FAST; //check if something new was rendered. If so render counter has greater value than //transfer counter. if( (renderFrameCounter != transferedFrameCounter) && (!rendering) ) { // LOG_INFO("[ServiceGUI]Sending buffer"); sendBuffer(); } else { // LOG_INFO(" NO new buffer to send"); } //check if there are pending commands to render. if( commands.empty() == false ) { LOG_ERROR("Rendering pending %d commands", commands.size()); sendToRender(); } } break; }; return std::make_shared(); } // Invoked when timer ticked void ServiceGUI::TickHandler(uint32_t id) { } // Invoked during initialization sys::ReturnCodes ServiceGUI::InitHandler() { //set function for acquiring time in seconds from the system gui::setTimeFunction( getTimeFunction ); //create semaphore to protect vector with commands waiting for rendering semCommands = xSemaphoreCreateBinary(); if( semCommands == NULL ) { LOG_FATAL("Failed to create commands semaphore."); return sys::ReturnCodes::Failure; } xSemaphoreGive( semCommands ); //initialize gui worker worker = new WorkerGUI( this ); std::list list; worker->init( list ); worker->run(); if( einkReady == false ) { requestSent = true; auto msg = std::make_shared(MessageType::EinkStateRequest ); sys::Bus::SendUnicast(msg, "ServiceEink", this); } return sys::ReturnCodes::Success; } sys::ReturnCodes ServiceGUI::DeinitHandler() { if( semCommands != NULL ) vSemaphoreDelete( semCommands ); semCommands = NULL; return sys::ReturnCodes::Success; } sys::ReturnCodes ServiceGUI::SwitchPowerModeHandler(const sys::ServicePowerMode mode) { LOG_FATAL("[ServiceGUI] PowerModeHandler: %d", static_cast(mode)); switch (mode){ case sys::ServicePowerMode ::Active: break; case sys::ServicePowerMode ::SuspendToRAM: case sys::ServicePowerMode ::SuspendToNVM: break; } return sys::ReturnCodes::Success; } } /* namespace sgui */