mirror of
https://github.com/mudita/MuditaOS.git
synced 2026-04-19 06:30:46 -04:00
285 lines
10 KiB
C++
285 lines
10 KiB
C++
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
|
|
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
|
|
|
|
#include <memory>
|
|
|
|
// module-os
|
|
#include "FreeRTOS.h"
|
|
#include "semphr.h"
|
|
|
|
// module-gui
|
|
#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"
|
|
|
|
#include "memory/usermem.h"
|
|
|
|
#include "SystemManager/SystemManager.hpp"
|
|
#include "WorkerGUI.hpp"
|
|
#include <FontManager.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("assets");
|
|
|
|
// load images
|
|
gui::ImageManager &imageManager = gui::ImageManager::getInstance();
|
|
imageManager.init("assets");
|
|
}
|
|
|
|
ServiceGUI::~ServiceGUI()
|
|
{
|
|
LOG_INFO("[ServiceGUI] Cleaning resources");
|
|
if (renderContext)
|
|
delete renderContext;
|
|
if (transferContext)
|
|
delete transferContext;
|
|
}
|
|
|
|
void ServiceGUI::sendBuffer()
|
|
{
|
|
// copy data from render context to transfer context
|
|
transferContext->insert(0, 0, renderContext);
|
|
|
|
auto msg = std::make_shared<seink::ImageMessage>(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<uint32_t>(WorkerGUICommands::Render), NULL);
|
|
}
|
|
|
|
// Invoked upon receiving data message
|
|
sys::Message_t ServiceGUI::DataReceivedHandler(sys::DataMessage *msgl, sys::ResponseMessage *resp)
|
|
{
|
|
|
|
sgui::GUIMessage *msg = static_cast<sgui::GUIMessage *>(msgl);
|
|
|
|
switch (msg->messageType) {
|
|
case MessageType::MessageTypeUninitialized: {
|
|
// LOG_ERROR("[ServiceGUI] Received uninitialized message type");
|
|
} break;
|
|
case MessageType::GUICommands: {
|
|
// LOG_INFO("[%s] GUICommands", GetName().c_str());
|
|
auto dmsg = static_cast<sgui::DrawMessage *>(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 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<seink::EinkMessage>(MessageType::EinkStateRequest);
|
|
sys::Bus::SendUnicast(msg, "ServiceEink", this);
|
|
}
|
|
} break;
|
|
case MessageType::GUIFocusInfo: {
|
|
// LOG_INFO("[%s] GUIFocusInfo", GetName().c_str());
|
|
} break;
|
|
case 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_INFO("Rendering pending %d commands", commands.size());
|
|
sendToRender();
|
|
}
|
|
} break;
|
|
default:
|
|
break;
|
|
};
|
|
|
|
return std::make_shared<sys::ResponseMessage>();
|
|
}
|
|
|
|
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<sys::WorkerQueueInfo> list;
|
|
worker->init(list);
|
|
worker->run();
|
|
|
|
if (einkReady == false) {
|
|
requestSent = true;
|
|
auto msg = std::make_shared<seink::EinkMessage>(MessageType::EinkStateRequest);
|
|
sys::Bus::SendUnicast(msg, "ServiceEink", this);
|
|
}
|
|
return sys::ReturnCodes::Success;
|
|
}
|
|
|
|
sys::ReturnCodes ServiceGUI::DeinitHandler()
|
|
{
|
|
|
|
if (semCommands != NULL)
|
|
vSemaphoreDelete(semCommands);
|
|
semCommands = NULL;
|
|
|
|
worker->stop();
|
|
worker->join();
|
|
worker->deinit();
|
|
|
|
return sys::ReturnCodes::Success;
|
|
}
|
|
|
|
sys::ReturnCodes ServiceGUI::SwitchPowerModeHandler(const sys::ServicePowerMode mode)
|
|
{
|
|
LOG_FATAL("[ServiceGUI] PowerModeHandler: %s", c_str(mode));
|
|
|
|
switch (mode) {
|
|
case sys::ServicePowerMode ::Active:
|
|
break;
|
|
case sys::ServicePowerMode ::SuspendToRAM:
|
|
case sys::ServicePowerMode ::SuspendToNVM:
|
|
break;
|
|
}
|
|
|
|
return sys::ReturnCodes::Success;
|
|
}
|
|
|
|
} /* namespace sgui */
|