~aleteoryx/muditaos

8bab7f0031bc8b0c66e9d1dc2f1c3309a6630bc9 — Adam 5 years ago ac2c954
EGD-3585 ServiceGUI and ServiceEink initial cleanup (#953)

* EGD-3585 SerwiceGUI and ServiceEink initial cleanup

* moved code to one function per message handling
* removed dead code from comments
* added Service.cpp demangling to debug messages for clear output
* added Response value to return messages to `connect()`
31 files changed, 425 insertions(+), 462 deletions(-)

M module-apps/Application.cpp
M module-apps/application-settings/windows/EinkModeWindow.cpp
M module-gui/gui/CMakeLists.txt
M module-gui/gui/Common.hpp
D module-gui/gui/GuiCommon.cpp
M module-gui/gui/core/DrawCommand.hpp
M module-services/service-appmgr/ApplicationManager.cpp
A module-services/service-eink/Common.hpp
M module-services/service-eink/ServiceEink.cpp
M module-services/service-eink/ServiceEink.hpp
A module-services/service-eink/messages/EinkDMATransfer.hpp
M module-services/service-eink/messages/EinkMessage.hpp
M module-services/service-eink/messages/EinkModeMessage.hpp
M module-services/service-eink/messages/ImageMessage.cpp
M module-services/service-eink/messages/ImageMessage.hpp
A module-services/service-eink/messages/StateRequest.hpp
A module-services/service-eink/messages/TemperatureUpdate.hpp
M module-services/service-gui/CMakeLists.txt
A module-services/service-gui/Common.hpp
M module-services/service-gui/ServiceGUI.cpp
M module-services/service-gui/ServiceGUI.hpp
M module-services/service-gui/WorkerGUI.cpp
M module-services/service-gui/WorkerGUI.hpp
M module-services/service-gui/messages/DrawMessage.cpp
M module-services/service-gui/messages/DrawMessage.hpp
A module-services/service-gui/messages/GUIDisplayReady.hpp
M module-services/service-gui/messages/GUIMessage.hpp
A module-services/service-gui/messages/RenderingFinished.hpp
M module-sys/Service/Service.cpp
M module-utils/taglib
M source/MessageType.hpp
M module-apps/Application.cpp => module-apps/Application.cpp +2 -1
@@ 30,6 30,7 @@
#include <iterator>                                      // for distance, next
#include <type_traits>                                   // for add_const<>...
#include <WindowsFactory.hpp>
#include <service-gui/Common.hpp>

namespace gui
{


@@ 137,7 138,7 @@ namespace app
            else if (suspendInProgress) {
                message->setCommandType(sgui::DrawMessage::Type::SUSPEND);
            }
            sys::Bus::SendUnicast(message, "ServiceGUI", this);
            sys::Bus::SendUnicast(message, service::name::gui, this);
        }

        if (suspendInProgress)

M module-apps/application-settings/windows/EinkModeWindow.cpp => module-apps/application-settings/windows/EinkModeWindow.cpp +9 -6
@@ 4,6 4,7 @@
#include <memory>
#include <functional>

#include "Common.hpp"
#include "messages/EinkModeMessage.hpp"
#include "service-appmgr/Controller.hpp"



@@ 32,16 33,18 @@ namespace gui
        setTitle("Change eink mode");
        auto label               = new Label(this, 100, 200, 300, 50, "Change mode on click");
        label->activatedCallback = [this](Item &) -> bool {
            static auto last_mode = seink::EinkModeMessage::Mode::Normal;
            if (last_mode == seink::EinkModeMessage::Mode::Normal) {
                last_mode = seink::EinkModeMessage::Mode::Invert;
            static auto last_mode = service::eink::EinkModeMessage::Mode::Normal;
            if (last_mode == service::eink::EinkModeMessage::Mode::Normal) {
                last_mode = service::eink::EinkModeMessage::Mode::Invert;
            }
            else {
                last_mode = seink::EinkModeMessage::Mode::Normal;
                last_mode = service::eink::EinkModeMessage::Mode::Normal;
            }

            sys::Bus::SendUnicast(
                std::make_shared<seink::EinkModeMessage>(last_mode), "ServiceEink", this->application, 5000);
            sys::Bus::SendUnicast(std::make_shared<service::eink::EinkModeMessage>(last_mode),
                                  service::name::eink,
                                  this->application,
                                  5000);
            return true;
        };
        setFocusItem(label);

M module-gui/gui/CMakeLists.txt => module-gui/gui/CMakeLists.txt +0 -2
@@ 18,8 18,6 @@ target_include_directories( ${PROJECT_NAME}

target_sources(${PROJECT_NAME}

    PRIVATE
        "${CMAKE_CURRENT_LIST_DIR}/GuiCommon.cpp"
    PUBLIC
        "${CMAKE_CURRENT_LIST_DIR}/Common.hpp"
        "${CMAKE_CURRENT_LIST_DIR}/SwitchData.hpp"

M module-gui/gui/Common.hpp => module-gui/gui/Common.hpp +0 -5
@@ 137,11 137,6 @@ namespace gui
        BottomRight = 0x80,
    };

    typedef uint32_t (*timeSecondsFunctionPtr)();

    uint32_t getTime();
    void setTimeFunction(timeSecondsFunctionPtr fptr);

} // namespace gui

inline const char *c_str(gui::RefreshModes refresh)

D module-gui/gui/GuiCommon.cpp => module-gui/gui/GuiCommon.cpp +0 -49
@@ 1,49 0,0 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

/*
 * Common.cpp
 *
 *  Created on: 8 maj 2019
 *      Author: robert
 */
#include <cstdint>
#include <type_traits>
#include "Common.hpp"

namespace gui
{

    static timeSecondsFunctionPtr timeSecondsFunction = nullptr;

    bool operator&(const gui::RectangleEdge &lhs, const gui::RectangleEdge &rhs)
    {
        using T = std::underlying_type_t<RectangleEdge>;
        return static_cast<bool>(static_cast<T>(lhs) & static_cast<T>(rhs));
    }

    RectangleEdge operator|(const RectangleEdge &lhs, const RectangleEdge &rhs)
    {
        using T = std::underlying_type_t<RectangleEdge>;
        return static_cast<RectangleEdge>(static_cast<T>(lhs) | static_cast<T>(rhs));
    }

    RectangleEdge operator|=(RectangleEdge &lhs, const RectangleEdge &rhs)
    {
        lhs = lhs | rhs;
        return lhs;
    }

    uint32_t getTime()
    {
        if (timeSecondsFunction != nullptr)
            return timeSecondsFunction();
        return 0;
    }

    void setTimeFunction(timeSecondsFunctionPtr fptr)
    {
        timeSecondsFunction = fptr;
    }

} // namespace gui

M module-gui/gui/core/DrawCommand.hpp => module-gui/gui/core/DrawCommand.hpp +2 -3
@@ 5,12 5,11 @@

#include <string>
#include <cstdint>

#include <module-utils/math/Math.hpp>
#include <utf8/UTF8.hpp>
#include <gui/Common.hpp>

#include "utf8/UTF8.hpp"
#include "Color.hpp"
#include "Common.hpp"

namespace gui
{

M module-services/service-appmgr/ApplicationManager.cpp => module-services/service-appmgr/ApplicationManager.cpp +11 -11
@@ 15,7 15,9 @@
#include "Service/Timer.hpp"               // for Timer, Timer::Type, Timer::Type::SingleShot, ms
#include "service-db/api/DBServiceAPI.hpp" // for DBServiceAPI
#include "service-evtmgr/EventManager.hpp" // for EventManager
#include "service-eink/ServiceEink.hpp"    // for ServiceEink
#include <service-eink/ServiceEink.hpp>    // for ServiceEink
#include <service-eink/Common.hpp>
#include <service-gui/Common.hpp>
#include "service-gui/ServiceGUI.hpp"      // for ServiceGUI
#include "log/log.hpp"                     // for LOG_INFO, LOG_ERROR, LOG_WARN, LOG_DEBUG, LOG_FATAL
#include "Common.hpp"                      // for ShowMode, ShowMode::GUI_SHOW_INIT


@@ 32,8 34,6 @@ namespace app::manager
{
    namespace
    {
        constexpr char GUIServiceName[]                = "ServiceGUI";
        constexpr char EInkServiceName[]               = "ServiceEink";
        constexpr auto default_application_locktime_ms = 30000;

        utils::Lang toUtilsLanguage(SettingsLanguage language)


@@ 219,12 219,12 @@ namespace app::manager
    void ApplicationManager::startSystemServices()
    {
        if (bool ret = sys::SystemManager::CreateService(
                std::make_shared<sgui::ServiceGUI>(GUIServiceName, GetName(), 480, 600), this);
                std::make_shared<sgui::ServiceGUI>(service::name::gui, GetName(), 480, 600), this);
            !ret) {
            LOG_ERROR("Failed to initialize GUI service");
        }
        if (bool ret =
                sys::SystemManager::CreateService(std::make_shared<ServiceEink>(EInkServiceName, GetName()), this);
                sys::SystemManager::CreateService(std::make_shared<ServiceEink>(service::name::eink, GetName()), this);
            !ret) {
            LOG_ERROR("Failed to initialize EInk service");
        }


@@ 232,8 232,8 @@ namespace app::manager

    void ApplicationManager::suspendSystemServices()
    {
        sys::SystemManager::SuspendService(GUIServiceName, this);
        sys::SystemManager::SuspendService(EInkServiceName, this);
        sys::SystemManager::SuspendService(service::name::gui, this);
        sys::SystemManager::SuspendService(service::name::eink, this);
    }

    void ApplicationManager::startBackgroundApplications()


@@ 327,8 327,8 @@ namespace app::manager

        switch (mode) {
        case sys::ServicePowerMode ::Active:
            sys::SystemManager::ResumeService(EInkServiceName, this);
            sys::SystemManager::ResumeService(GUIServiceName, this);
            sys::SystemManager::ResumeService(service::name::eink, this);
            sys::SystemManager::ResumeService(service::name::gui, this);
            break;
        case sys::ServicePowerMode ::SuspendToRAM:
            [[fallthrough]];


@@ 355,8 355,8 @@ namespace app::manager

    auto ApplicationManager::closeServices() -> bool
    {
        closeService(GUIServiceName);
        closeService(EInkServiceName);
        closeService(service::name::gui);
        closeService(service::name::eink);
        return true;
    }


A module-services/service-eink/Common.hpp => module-services/service-eink/Common.hpp +9 -0
@@ 0,0 1,9 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

namespace service::name
{
    constexpr char eink[] = "ServiceEink";
};

M module-services/service-eink/ServiceEink.cpp => module-services/service-eink/ServiceEink.cpp +132 -157
@@ 3,6 3,11 @@

#include "ServiceEink.hpp"
#include "messages/EinkModeMessage.hpp"
#include "messages/EinkDMATransfer.hpp"
#include "messages/StateRequest.hpp"
#include "messages/TemperatureUpdate.hpp"
#include <service-gui/Common.hpp>
#include <service-gui/messages/GUIDisplayReady.hpp>
#include <time/ScopedTime.hpp>

#include <log/log.hpp>


@@ 28,131 33,59 @@ enum class EinkWorkerCommands
};

ServiceEink::ServiceEink(const std::string &name, std::string parent)
    : sys::Service(name, parent, 4096 + 1024), selfRefereshTriggerCount{0}, temperatureMeasurementTriggerCount{0},
      powerOffTriggerCount{0}, powerOffTimer("PwrOffTimer", this, 3000, sys::Timer::Type::SingleShot)
    : sys::Service(name, parent, 4096 + 1024),
      einkRenderBuffer(std::make_unique<uint8_t[]>(screen.height * screen.width)), selfRefereshTriggerCount{0},
      temperatureMeasurementTriggerCount{0}, powerOffTriggerCount{0},
      powerOffTimer("PwrOffTimer", this, 3000, sys::Timer::Type::SingleShot)
{
    // initialize initial eink parameters
    memset(&waveformSettings, 0, sizeof(EinkWaveFormSettings_t));
    waveformSettings.mode        = EinkWaveformGC16;
    waveformSettings.temperature = -1000;

    connect(typeid(seink::EinkModeMessage),
    connect(typeid(service::eink::EinkModeMessage),
            [this](sys::DataMessage *message, sys::ResponseMessage *) -> sys::Message_t {
                auto msg          = static_cast<seink::EinkModeMessage *>(message);
                this->displayMode = msg->getMode() == seink::EinkModeMessage::Mode::Normal
                auto msg          = static_cast<service::eink::EinkModeMessage *>(message);
                this->displayMode = msg->getMode() == service::eink::EinkModeMessage::Mode::Normal
                                        ? EinkDisplayColorMode_e::EinkDisplayColorModeStandard
                                        : EinkDisplayColorMode_e::EinkDisplayColorModeInverted;
                return sys::Message_t();
            });

    connect(typeid(service::eink::EinkDMATransfer),
            [&](sys::DataMessage *request, sys::ResponseMessage *) -> sys::Message_t {
                return handleEinkDMATransfer(request);
            });

    connect(typeid(service::eink::ImageMessage),
            [&](sys::DataMessage *request, sys::ResponseMessage *) -> sys::Message_t {
                return handleImageMessage(request);
            });

    connect(typeid(service::eink::StateRequest),
            [&](sys::DataMessage *request, sys::ResponseMessage *) -> sys::Message_t {
                return handleStateRequest(request);
            });

    connect(typeid(service::eink::TemperatureUpdate),
            [&](sys::DataMessage *request, sys::ResponseMessage *) -> sys::Message_t {
                return handleTemperatureUpdate(request);
            });
}

ServiceEink::~ServiceEink()
{
    //	LOG_INFO("[ServiceEink] Cleaning resources");
    // release data from settings
    if (waveformSettings.LUTCData != nullptr)
        delete[] waveformSettings.LUTCData;
    if (waveformSettings.LUTDData != nullptr)
        delete[] waveformSettings.LUTDData;
    // set sure that any new temperature will cause to load data from file.
    waveformSettings.temperature = -1000;
}

// Invoked upon receiving data message
sys::Message_t ServiceEink::DataReceivedHandler(sys::DataMessage *msgl, sys::ResponseMessage *resp)
{
    seink::EinkMessage *msg = static_cast<seink::EinkMessage *>(msgl);

    switch (msg->messageType) {

    case MessageType::EinkImageData: {
        powerOffTimer.stop();
        auto dmsg = static_cast<seink::ImageMessage *>(msgl);
        //			LOG_INFO("[%s] EinkImageData", GetName().c_str());
        memcpy(einkRenderBuffer, dmsg->getData(), dmsg->getSize());
        deepRefresh = dmsg->getDeepRefresh();

        shutdownInProgress = dmsg->getShutdown();
        if (shutdownInProgress)
            LOG_DEBUG("Shutdown In Progress");

        suspendInProgress = dmsg->getSuspend();
        if (suspendInProgress)
            LOG_DEBUG("Suspend In Progress");
        auto msg = std::make_shared<sgui::GUIMessage>(MessageType::EinkDMATransfer);
        sys::Bus::SendUnicast(msg, this->GetName(), this);
    } break;
    case MessageType::EinkDMATransfer: {
        utils::time::Scoped scopedtimming("EinkDMATransfer");

        if (suspended) {
            if (suspendInProgress) {
                LOG_ERROR("drawing before suspend failed");
                suspendInProgress = false;
            }

            LOG_INFO("[ServiceEink] Received image while suspended, ignoring");
        }

        else {
            EinkPowerOn();

            int32_t temperature = EinkGetTemperatureInternal();
            //			LOG_INFO("temperature: %d", temperature );

            EinkStatus_e ret;
            if (deepRefresh) {
                changeWaveform(EinkWaveforms_e::EinkWaveformGC16, temperature);
                EinkDitherDisplay();
            }
            else {
                changeWaveform(EinkWaveforms_e::EinkWaveformDU2, temperature);
            }

            ret = EinkUpdateFrame(0, 0, 480, 600, einkRenderBuffer, Eink4Bpp, displayMode);
            if (ret != EinkOK)
                LOG_FATAL("Failed to update frame");

            if (deepRefresh) {
                //				LOG_INFO("EinkDisplayTimingsDeepCleanMode");
                ret = EinkRefreshImage(0, 0, 480, 600, EinkDisplayTimingsDeepCleanMode);
            }
            else {
                //				LOG_INFO("EinkDisplayTimingsFastRefreshMode");
                ret = EinkRefreshImage(0, 0, 480, 600, EinkDisplayTimingsFastRefreshMode);
            }

            if (ret != EinkOK)
                LOG_FATAL("Failed to refresh frame");

            powerOffTimer.reload();

            auto msg =
                std::make_shared<sgui::GUIMessage>(MessageType::GUIDisplayReady, suspendInProgress, shutdownInProgress);
            suspendInProgress  = false;
            shutdownInProgress = false;
            sys::Bus::SendUnicast(msg, "ServiceGUI", this);
        }
    } break;

    case MessageType::EinkTemperatureUpdate: {
        //			LOG_INFO("[%s] EinkTemperatureUpdate", GetName().c_str());
    } break;

    case MessageType::EinkStateRequest: {
        //			LOG_INFO("[%s] EinkStateRequest", GetName().c_str());
        auto msg = std::make_shared<sgui::GUIMessage>(MessageType::GUIDisplayReady);
        sys::Bus::SendUnicast(msg, "ServiceGUI", this);
    } break;

    default:
        break;
    };

    return std::make_shared<sys::ResponseMessage>();
}

// Invoked during initialization
sys::ReturnCodes ServiceEink::InitHandler()
{



@@ 164,29 97,10 @@ sys::ReturnCodes ServiceEink::InitHandler()
        LOG_FATAL("Error: Could not initialize Eink display!\n");
    }

    // TODO remove screen clearing code below.
    EinkPowerOn();

    //	uint8_t s_einkAmbientTemperature = EinkGetTemperatureInternal();
    //	LOG_INFO("EInk measured temperature: %d\u00B0C", s_einkAmbientTemperature);
    //
    //	// Make the saturation to the lower limit
    //	if (s_einkAmbientTemperature < 0)
    //		s_einkAmbientTemperature = 0;
    //
    //	// Make the saturation to the higher limit
    //	if (s_einkAmbientTemperature > 49)
    //		s_einkAmbientTemperature = 49;
    //
    //	// Clear the temperature timer count
    //	deepClearScreen( s_einkAmbientTemperature );
    //	EinkPowerOff();
    //
    //	//TODO remove and add timer or turning off eink
    //	EinkPowerOn();

    auto msg = std::make_shared<sgui::GUIMessage>(MessageType::GUIDisplayReady);
    sys::Bus::SendUnicast(msg, "ServiceGUI", this);
    auto msg = std::make_shared<service::gui::GUIDisplayReady>(suspendInProgress, shutdownInProgress);
    sys::Bus::SendUnicast(msg, service::name::gui, this);

    return sys::ReturnCodes::Success;
}


@@ 210,30 124,8 @@ sys::ReturnCodes ServiceEink::SwitchPowerModeHandler(const sys::ServicePowerMode
            LOG_FATAL("Error: Could not initialize Eink display!\n");
        }

        // TODO remove screen clearing code below.
        EinkPowerOn();

        //            int32_t temperature = EinkGetTemperatureInternal();
        //            	//			LOG_INFO("temperature: %d", temperature );
        //
        //			changeWaveform(EinkWaveforms_e::EinkWaveformGC16, temperature);
        //			EinkDitherDisplay();
        //
        //            EinkRefreshImage (0, 0, 480, 600, EinkDisplayTimingsDeepCleanMode );

        EinkPowerOff();
        //
        //            uint8_t s_einkAmbientTemperature = EinkGetTemperatureInternal();
        //            // Make the saturation to the lower limit
        //            if (s_einkAmbientTemperature < 0)
        //                s_einkAmbientTemperature = 0;
        //
        //            // Make the saturation to the higher limit
        //            if (s_einkAmbientTemperature > 49)
        //                s_einkAmbientTemperature = 49;
        //
        //            // Clear the temperature timer count
        //            deepClearScreen( s_einkAmbientTemperature );
    } break;
    case sys::ServicePowerMode ::SuspendToRAM:
    case sys::ServicePowerMode ::SuspendToNVM:


@@ 249,7 141,6 @@ sys::ReturnCodes ServiceEink::SwitchPowerModeHandler(const sys::ServicePowerMode
bool ServiceEink::changeWaveform(EinkWaveforms_e mode, const int32_t temperature)
{

    // If neither the temperature nor the waveform has changed - do nothing
    if ((temperature == waveformSettings.temperature) && (mode == waveformSettings.mode)) {
        return EinkOK;
    }


@@ 302,13 193,12 @@ bool ServiceEink::changeWaveform(EinkWaveforms_e mode, const int32_t temperature
        break;
    }

    // Open file
    auto file = vfs.fopen("Luts.bin", "rb");
    if (file == nullptr) {
        LOG_FATAL("Could not find the LUTS.bin file. Returning");
        return false;
    }
    // Allocate memory for the LUTD data. +1 for the EinkLUTD command for SPI transfer

    if (waveformSettings.LUTDData != nullptr)
        delete[] waveformSettings.LUTDData;



@@ 321,7 211,6 @@ bool ServiceEink::changeWaveform(EinkWaveforms_e mode, const int32_t temperature
        return false;
    }

    // Allocate memory for the LUTC data. +1 for the EinkLUTC command for SPI transfer
    if (waveformSettings.LUTCData != nullptr)
        delete[] waveformSettings.LUTCData;



@@ 336,7 225,6 @@ bool ServiceEink::changeWaveform(EinkWaveforms_e mode, const int32_t temperature
    waveformSettings.LUTDData[0] = EinkLUTD;
    waveformSettings.LUTCData[0] = EinkLUTC;

    /// LUTD
    vfs.fseek(file, offset, SEEK_SET);
    vfs.fread(&waveformSettings.LUTDData[1], 1, LUTD_SIZE, file);



@@ 345,7 233,6 @@ bool ServiceEink::changeWaveform(EinkWaveforms_e mode, const int32_t temperature
        frameCount * 64 + 1 +
        1; // (frameCount * 64) - size of actual LUT; (+1) - the byte containing frameCount; (+1) - EinkLUTD command

    // LUTC
    offset += LUTD_SIZE;
    vfs.fseek(file, offset, SEEK_SET);
    vfs.fread(&waveformSettings.LUTCData[1], 1, LUTC_SIZE, file);


@@ 365,28 252,34 @@ bool ServiceEink::deepClearScreen(int8_t temperature)
    changeWaveform(EinkWaveforms_e::EinkWaveformA2, temperature);

    EinkStatus_e ret;
    memset(einkRenderBuffer, 15, 480 * 600);
    ret = EinkUpdateFrame(0, 0, 480, 600, einkRenderBuffer, Eink4Bpp, displayMode);
    memset(einkRenderBuffer.get(), 15, screen.width * screen.height);
    ret = EinkUpdateFrame(
        pointTopLeft.x, pointTopLeft.y, screen.width, screen.height, einkRenderBuffer.get(), Eink4Bpp, displayMode);
    if (ret != EinkOK)
        LOG_FATAL("Failed to update frame");
    ret = EinkRefreshImage(0, 0, 480, 600, EinkDisplayTimingsFastRefreshMode);
    ret = EinkRefreshImage(
        pointTopLeft.x, pointTopLeft.y, screen.width, screen.height, EinkDisplayTimingsFastRefreshMode);
    if (ret != EinkOK)
        LOG_FATAL("Failed to refresh frame");

    for (uint32_t i = 0; i < 2; i++) {
        memset(einkRenderBuffer, 0, 480 * 600);
        ret = EinkUpdateFrame(0, 0, 480, 600, einkRenderBuffer, Eink4Bpp, displayMode);
        memset(einkRenderBuffer.get(), 0, screen.width * screen.height);
        ret = EinkUpdateFrame(
            pointTopLeft.x, pointTopLeft.y, screen.width, screen.height, einkRenderBuffer.get(), Eink4Bpp, displayMode);
        if (ret != EinkOK)
            LOG_FATAL("Failed to update frame");
        ret = EinkRefreshImage(0, 0, 480, 600, EinkDisplayTimingsFastRefreshMode);
        ret = EinkRefreshImage(
            pointTopLeft.x, pointTopLeft.y, screen.width, screen.height, EinkDisplayTimingsFastRefreshMode);
        if (ret != EinkOK)
            LOG_FATAL("Failed to refresh frame");

        memset(einkRenderBuffer, 15, 480 * 600);
        ret = EinkUpdateFrame(0, 0, 480, 600, einkRenderBuffer, Eink4Bpp, displayMode);
        memset(einkRenderBuffer.get(), 15, screen.width * screen.height);
        ret = EinkUpdateFrame(
            pointTopLeft.x, pointTopLeft.y, screen.width, screen.height, einkRenderBuffer.get(), Eink4Bpp, displayMode);
        if (ret != EinkOK)
            LOG_FATAL("Failed to update frame");
        ret = EinkRefreshImage(0, 0, 480, 600, EinkDisplayTimingsFastRefreshMode);
        ret = EinkRefreshImage(
            pointTopLeft.x, pointTopLeft.y, screen.width, screen.height, EinkDisplayTimingsFastRefreshMode);
        if (ret != EinkOK)
            LOG_FATAL("Failed to refresh frame");
    }


@@ 397,3 290,85 @@ bool ServiceEink::deepClearScreen(int8_t temperature)

    return true;
}

sys::Message_t ServiceEink::handleEinkDMATransfer(sys::Message *message)
{
    utils::time::Scoped scopedtimming("EinkDMATransfer");

    if (suspended) {
        if (suspendInProgress) {
            LOG_ERROR("drawing before suspend failed");
            suspendInProgress = false;
        }

        LOG_INFO("[ServiceEink] Received image while suspended, ignoring");
    }
    else {
        EinkPowerOn();

        int32_t temperature = EinkGetTemperatureInternal();

        EinkStatus_e ret;
        if (deepRefresh) {
            changeWaveform(EinkWaveforms_e::EinkWaveformGC16, temperature);
            EinkDitherDisplay();
        }
        else {
            changeWaveform(EinkWaveforms_e::EinkWaveformDU2, temperature);
        }

        ret = EinkUpdateFrame(
            pointTopLeft.x, pointTopLeft.y, screen.width, screen.height, einkRenderBuffer.get(), Eink4Bpp, displayMode);
        if (ret != EinkOK)
            LOG_FATAL("Failed to update frame");

        if (deepRefresh) {
            ret = EinkRefreshImage(
                pointTopLeft.x, pointTopLeft.y, screen.width, screen.height, EinkDisplayTimingsDeepCleanMode);
        }
        else {
            ret = EinkRefreshImage(
                pointTopLeft.x, pointTopLeft.y, screen.width, screen.height, EinkDisplayTimingsFastRefreshMode);
        }

        if (ret != EinkOK)
            LOG_FATAL("Failed to refresh frame");

        powerOffTimer.reload();

        auto msg           = std::make_shared<service::gui::GUIDisplayReady>(suspendInProgress, shutdownInProgress);
        suspendInProgress  = false;
        shutdownInProgress = false;
        sys::Bus::SendUnicast(msg, service::name::gui, this);
    }
    return nullptr;
}

sys::Message_t ServiceEink::handleImageMessage(sys::Message *request)
{
    auto message = static_cast<service::eink::ImageMessage *>(request);

    powerOffTimer.stop();
    memcpy(einkRenderBuffer.get(), message->getData(), message->getSize());
    deepRefresh = message->getDeepRefresh();

    shutdownInProgress = message->getShutdown();
    if (shutdownInProgress)
        LOG_DEBUG("Shutdown In Progress");

    suspendInProgress = message->getSuspend();
    if (suspendInProgress)
        LOG_DEBUG("Suspend In Progress");
    sys::Bus::SendUnicast(std::make_shared<service::eink::EinkDMATransfer>(), GetName(), this);
    return nullptr;
}

sys::Message_t ServiceEink::handleStateRequest(sys::Message *)
{
    return std::make_shared<service::gui::GUIDisplayReady>(suspendInProgress, shutdownInProgress);
}

sys::Message_t ServiceEink::handleTemperatureUpdate(sys::Message *)
{
    return nullptr;
}

M module-services/service-eink/ServiceEink.hpp => module-services/service-eink/ServiceEink.hpp +12 -6
@@ 3,6 3,7 @@

#pragma once

#include <gui/Common.hpp>
#include <EinkIncludes.hpp>
#include <Service/Common.hpp>
#include <Service/Message.hpp>


@@ 14,6 15,10 @@

class ServiceEink : public sys::Service
{
    gui::Size screen        = {480, 600};
    gui::Point pointTopLeft = {0, 0};
    std::unique_ptr<uint8_t[]> einkRenderBuffer;

  protected:
    // counts timer triggers from last self refresh
    uint32_t selfRefereshTriggerCount;


@@ 43,7 48,6 @@ class ServiceEink : public sys::Service

    bool deepClearScreen(int8_t temperature);

    uint8_t einkRenderBuffer[600 * 480];
    bool deepRefresh         = false;

    sys::ms powerOffTime = 3000;


@@ 51,14 55,16 @@ class ServiceEink : public sys::Service

  public:
    ServiceEink(const std::string &name, std::string parent = "");
    ~ServiceEink();
    ~ServiceEink() override;

    sys::Message_t DataReceivedHandler(sys::DataMessage *msgl, sys::ResponseMessage *resp) override;

    // Invoked during initialization
    sys::ReturnCodes InitHandler() override;

    sys::ReturnCodes DeinitHandler() override;

    sys::ReturnCodes SwitchPowerModeHandler(const sys::ServicePowerMode mode) override final;

  private:
    sys::Message_t handleEinkDMATransfer(sys::Message *message);
    sys::Message_t handleImageMessage(sys::Message *message);
    sys::Message_t handleStateRequest(sys::Message *messge);
    sys::Message_t handleTemperatureUpdate(sys::Message *);
};

A module-services/service-eink/messages/EinkDMATransfer.hpp => module-services/service-eink/messages/EinkDMATransfer.hpp +12 -0
@@ 0,0 1,12 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include "EinkMessage.hpp"

namespace service::eink
{
    class EinkDMATransfer : public service::eink::EinkMessage
    {};
}; // namespace service::eink

M module-services/service-eink/messages/EinkMessage.hpp => module-services/service-eink/messages/EinkMessage.hpp +2 -2
@@ 6,13 6,13 @@
#include <MessageType.hpp>
#include <Service/Message.hpp>

namespace seink
namespace service::eink
{

    class EinkMessage : public sys::DataMessage
    {
      public:
        EinkMessage(MessageType messageType) : sys::DataMessage(messageType){};
        EinkMessage() : sys::DataMessage(MessageType::EinkMessage){};
    };

} /* namespace seink */

M module-services/service-eink/messages/EinkModeMessage.hpp => module-services/service-eink/messages/EinkModeMessage.hpp +3 -2
@@ 5,7 5,7 @@

#include "EinkMessage.hpp"

namespace seink
namespace service::eink
{
    class EinkModeMessage : public EinkMessage
    {


@@ 15,7 15,8 @@ namespace seink
            Normal,
            Invert
        };
        EinkModeMessage(Mode mode) : EinkMessage(MessageType::EinkMessage), mode(mode)

        EinkModeMessage(Mode mode) : EinkMessage(), mode(mode)
        {}

        [[nodiscard]] auto getMode() const noexcept

M module-services/service-eink/messages/ImageMessage.cpp => module-services/service-eink/messages/ImageMessage.cpp +3 -7
@@ 6,16 6,12 @@

#include <MessageType.hpp>

namespace seink
namespace service::eink
{

    ImageMessage::ImageMessage(
        uint32_t x, uint32_t y, uint32_t w, uint32_t h, bool deepRefresh, uint8_t *data, bool suspend, bool shutdown)
        : EinkMessage(MessageType::EinkImageData), x{x}, y{y}, w{w}, h{h},
          deepRefresh{deepRefresh}, data{data}, suspend{suspend}, shutdown{shutdown}
        : EinkMessage(), x{x}, y{y}, w{w}, h{h}, deepRefresh{deepRefresh}, data{data}, suspend{suspend}, shutdown{
                                                                                                             shutdown}
    {}

    ImageMessage::~ImageMessage()
    {}

} /* namespace seink */

M module-services/service-eink/messages/ImageMessage.hpp => module-services/service-eink/messages/ImageMessage.hpp +1 -5
@@ 7,12 7,9 @@

#include <cstdint>

namespace seink
namespace service::eink
{

    /*
     *
     */
    class ImageMessage : public EinkMessage
    {
      protected:


@@ 31,7 28,6 @@ namespace seink
                     uint8_t *data,
                     bool suspend,
                     bool shutdown);
        virtual ~ImageMessage();

        uint8_t *getData()
        {

A module-services/service-eink/messages/StateRequest.hpp => module-services/service-eink/messages/StateRequest.hpp +12 -0
@@ 0,0 1,12 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include "EinkMessage.hpp"

namespace service::eink
{
    class StateRequest : public EinkMessage
    {};
} // namespace service::eink

A module-services/service-eink/messages/TemperatureUpdate.hpp => module-services/service-eink/messages/TemperatureUpdate.hpp +12 -0
@@ 0,0 1,12 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include "EinkMessage.hpp"

namespace service::eink
{
    class TemperatureUpdate : public EinkMessage
    {};
} // namespace service::eink

M module-services/service-gui/CMakeLists.txt => module-services/service-gui/CMakeLists.txt +0 -3
@@ 10,8 10,6 @@ set(SOURCES

add_library(${PROJECT_NAME} STATIC ${SOURCES})

#include_directories(module-gui)

target_link_libraries(${PROJECT_NAME}
    module-gui
    service-eink


@@ 21,7 19,6 @@ target_link_libraries(${PROJECT_NAME}
target_include_directories(${PROJECT_NAME}
    PRIVATE
        module-gui
        service-eink
    PUBLIC
        "${CMAKE_CURRENT_LIST_DIR}"
        "${CMAKE_CURRENT_LIST_DIR}/messages"

A module-services/service-gui/Common.hpp => module-services/service-gui/Common.hpp +9 -0
@@ 0,0 1,9 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

namespace service::name
{
    constexpr char gui[] = "ServiceGUI";
};

M module-services/service-gui/ServiceGUI.cpp => module-services/service-gui/ServiceGUI.cpp +116 -150
@@ 6,6 6,8 @@

#include "messages/GUIMessage.hpp"
#include "messages/DrawMessage.hpp"
#include "messages/RenderingFinished.hpp"
#include "messages/GUIDisplayReady.hpp"

#include <DrawCommand.hpp>
#include <FontManager.hpp>


@@ 13,13 15,15 @@
#include <gui/core/Context.hpp>
#include <gui/core/ImageManager.hpp>
#include <log/log.hpp>
#include <messages/EinkMessage.hpp>
#include <projdefs.h>
#include <service-appmgr/Controller.hpp>
#include <service-eink/messages/ImageMessage.hpp>
#include <service-eink/messages/EinkMessage.hpp>
#include <service-eink/messages/StateRequest.hpp>
#include <Service/Bus.hpp>
#include <Service/Worker.hpp>
#include <SystemManager/SystemManager.hpp>
#include <service-eink/Common.hpp>

#include <task.h>



@@ 36,12 40,6 @@ extern "C"

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},


@@ 50,17 48,28 @@ namespace sgui

        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");

        connect(typeid(sgui::DrawMessage), [&](sys::DataMessage *request, sys::ResponseMessage *) -> sys::Message_t {
            return handleDrawMessage(request);
        });

        connect(typeid(service::gui::RenderingFinished),
                [&](sys::DataMessage *request, sys::ResponseMessage *) -> sys::Message_t {
                    return handleGUIRenderingFinished(request);
                });

        connect(typeid(service::gui::GUIDisplayReady),
                [&](sys::DataMessage *request, sys::ResponseMessage *) -> sys::Message_t {
                    return handleGUIDisplayReady(request);
                });
    }

    ServiceGUI::~ServiceGUI()


@@ 74,23 83,22 @@ namespace sgui

    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);
        auto msg =
            std::make_shared<service::eink::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);
        auto ret  = sys::Bus::SendUnicast(msg, service::name::eink, this, 2000);
        if (ret.first == sys::ReturnCodes::Success) {
            transferedFrameCounter = renderFrameCounter;
        }
        // set default refreshing mode.
        mode = gui::RefreshModes::GUI_REFRESH_FAST;
    }



@@ 100,139 108,13 @@ namespace sgui
        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->type == sgui::DrawMessage::Type::SHUTDOWN) {
                        LOG_WARN("Shutdown - received shutdown draw commands");
                        shutdownInProgress = true;
                    }

                    // if message carries suspend flag set flag in service and proceed
                    if (dmsg->type == sgui::DrawMessage::Type::SUSPEND) {
                        LOG_WARN("Suspended - received suspend draw commands");
                        suspendInProgress = true;
                    }

                    // update mode
                    if (dmsg->mode == gui::RefreshModes::GUI_REFRESH_DEEP) {
                        mode = dmsg->mode;
                    }

                    // LOG_DEBUG("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 = std::move(dmsg->commands);
                        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.");

                app::manager::Controller::changePowerSaveMode(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.");


@@ 240,7 122,6 @@ namespace sgui
        }
        xSemaphoreGive(semCommands);

        // initialize gui worker
        worker = new WorkerGUI(this);
        std::list<sys::WorkerQueueInfo> list;
        worker->init(list);


@@ 248,8 129,7 @@ namespace sgui

        if (einkReady == false) {
            requestSent = true;
            auto msg    = std::make_shared<seink::EinkMessage>(MessageType::EinkStateRequest);
            sys::Bus::SendUnicast(msg, "ServiceEink", this);
            sys::Bus::SendUnicast(std::make_shared<service::eink::StateRequest>(), service::name::eink, this);
        }
        return sys::ReturnCodes::Success;
    }


@@ 283,4 163,90 @@ namespace sgui
        return sys::ReturnCodes::Success;
    }

    sys::Message_t ServiceGUI::handleDrawMessage(sys::Message *message)
    {
        auto dmsg = static_cast<sgui::DrawMessage *>(message);
        if (!dmsg->commands.empty()) {

            if (!suspendInProgress) {

                if (dmsg->isType(sgui::DrawMessage::Type::SHUTDOWN)) {
                    LOG_WARN("Shutdown - received shutdown draw commands");
                    shutdownInProgress = true;
                }

                if (dmsg->isType(sgui::DrawMessage::Type::SUSPEND)) {
                    LOG_WARN("Suspended - received suspend draw commands");
                    suspendInProgress = true;
                }

                if (dmsg->mode == gui::RefreshModes::GUI_REFRESH_DEEP) {
                    mode = dmsg->mode;
                }

                if (xSemaphoreTake(semCommands, pdMS_TO_TICKS(1000)) == pdTRUE) {
                    commands = std::move(dmsg->commands);
                    xSemaphoreGive(semCommands);
                }
                else {
                    LOG_ERROR("Failed to acquire semaphore");
                }

                if (!rendering) {
                    sendToRender();
                }
            }
            else {
                LOG_WARN("Suspended - ignoring draw commands");
            }
        }
        return nullptr;
    }

    sys::Message_t ServiceGUI::handleGUIRenderingFinished(sys::Message *message)
    {
        rendering = false;
        renderFrameCounter++;

        if (einkReady) {
            sendBuffer();
        }
        else if (!requestSent) {
            requestSent = true;
            sys::Bus::SendUnicast(std::make_shared<service::eink::StateRequest>(), service::name::eink, this);
        }
        return nullptr;
    }

    sys::Message_t ServiceGUI::handleGUIDisplayReady(sys::Message *message)
    {
        auto msg    = static_cast<service::gui::GUIDisplayReady *>(message);
        einkReady   = true;
        requestSent = false;

        if (msg->getShutdownInProgress()) {
            einkReady         = false;
            suspendInProgress = false;
            LOG_DEBUG("last rendering before shutdown finished.");

            sys::SystemManager::CloseSystem(this);
        }

        if (msg->getSuspendInProgress()) {
            einkReady         = false;
            suspendInProgress = false;
            LOG_DEBUG("last rendering before suspend is finished.");

            app::manager::Controller::changePowerSaveMode(this);
        }
        if ((renderFrameCounter != transferedFrameCounter) && (!rendering)) {
            sendBuffer();
        }

        if (commands.empty() == false) {
            sendToRender();
        }
        return nullptr;
    }

} /* namespace sgui */

M module-services/service-gui/ServiceGUI.hpp => module-services/service-gui/ServiceGUI.hpp +5 -2
@@ 4,13 4,11 @@
#pragma once

// module-gui
#include "WorkerGUI.hpp"

#include <Common.hpp>
#include <gui/core/Context.hpp>
#include <gui/core/Renderer.hpp>
#include <gui/input/Translator.hpp>
#include <messages/DrawMessage.hpp>
#include <queue.h>
#include <semphr.h>
#include <Service/Common.hpp>


@@ 93,6 91,11 @@ namespace sgui
        sys::ReturnCodes DeinitHandler() override;

        sys::ReturnCodes SwitchPowerModeHandler(const sys::ServicePowerMode mode) override final;

      private:
        sys::Message_t handleDrawMessage(sys::Message *message);
        sys::Message_t handleGUIRenderingFinished(sys::Message *message);
        sys::Message_t handleGUIDisplayReady(sys::Message *message);
    };

} /* namespace sgui */

M module-services/service-gui/WorkerGUI.cpp => module-services/service-gui/WorkerGUI.cpp +3 -13
@@ 19,6 19,7 @@
#include <memory>
#include <utility>
#include <vector>
#include "messages/RenderingFinished.hpp"

namespace sgui
{


@@ 30,18 31,13 @@ namespace sgui
    {
        QueueHandle_t queue = queues[queueID];

        sgui::ServiceGUI *serviceGUI = reinterpret_cast<sgui::ServiceGUI *>(service);
        auto serviceGUI = static_cast<sgui::ServiceGUI *>(service);

        // queue for receiving rendering commands
        if (queueID == 0) {
            //		LOG_INFO("Received rendering commands");

            sys::WorkerCommand received;
            xQueueReceive(queue, &received, 0);

            // take all unique pointers
            std::list<std::unique_ptr<gui::DrawCommand>> uniqueCommands;

            if (xSemaphoreTake(serviceGUI->semCommands, pdMS_TO_TICKS(1000)) == pdTRUE) {
                uniqueCommands = std::move(serviceGUI->commands);
                xSemaphoreGive(serviceGUI->semCommands);


@@ 50,15 46,9 @@ namespace sgui
                LOG_ERROR("Failed to acquire semaphore");
            }

            //		uint32_t start_tick = xTaskGetTickCount();
            serviceGUI->renderer.render(serviceGUI->renderContext, uniqueCommands);
            //		uint32_t end_tick = xTaskGetTickCount();
            //		LOG_INFO("[WorkerGUI] RenderingTime: %d", end_tick - start_tick);


            // notify gui service that rendering is complete
            auto message = std::make_shared<sys::DataMessage>(MessageType::GUIRenderingFinished);
            sys::Bus::SendUnicast(message, this->service->GetName(), this->service);
            sys::Bus::SendUnicast(std::make_shared<service::gui::RenderingFinished>(), service->GetName(), service);
        }
        return true;
    }

M module-services/service-gui/WorkerGUI.hpp => module-services/service-gui/WorkerGUI.hpp +0 -1
@@ 8,7 8,6 @@
#include <core/Context.hpp>
#include <core/DrawCommand.hpp>
#include <gui/core/Renderer.hpp>
#include <messages/GUIMessage.hpp>
#include <Service/Worker.hpp>

#include <cstdint>

M module-services/service-gui/messages/DrawMessage.cpp => module-services/service-gui/messages/DrawMessage.cpp +1 -1
@@ 10,7 10,7 @@ namespace sgui
{

    DrawMessage::DrawMessage(std::list<gui::Command> commands, gui::RefreshModes mode)
        : GUIMessage(MessageType::GUICommands), mode(mode), commands(std::move(commands))
        : GUIMessage(), mode(mode), commands(std::move(commands))
    {
    }
} /* namespace sgui */

M module-services/service-gui/messages/DrawMessage.hpp => module-services/service-gui/messages/DrawMessage.hpp +7 -3
@@ 27,19 27,23 @@ namespace sgui
            NORMAL,
            SUSPEND,
            SHUTDOWN
        };
        } type = Type::NORMAL;

      public:
        gui::RefreshModes mode;
        std::list<gui::Command> commands;
        Type type = Type::NORMAL;

        DrawMessage(std::list<gui::Command> commandsList, gui::RefreshModes mode);

        void setCommandType(Type type)
        void setCommandType(Type type) noexcept
        {
            this->type = type;
        }

        bool isType(Type type) const noexcept
        {
            return this->type == type;
        }
    };

} // namespace sgui

A module-services/service-gui/messages/GUIDisplayReady.hpp => module-services/service-gui/messages/GUIDisplayReady.hpp +31 -0
@@ 0,0 1,31 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include "GUIMessage.hpp"

namespace service::gui
{
    class GUIDisplayReady : public sgui::GUIMessage
    {

        bool suspendInProgress  = false;
        bool shutdownInProgress = false;

      public:
        GUIDisplayReady(bool suspendInProgress, bool shutdownInProgress)
            : suspendInProgress(suspendInProgress), shutdownInProgress(shutdownInProgress)
        {}

        [[nodiscard]] auto getSuspendInProgress() const noexcept
        {
            return suspendInProgress;
        }

        [[nodiscard]] auto getShutdownInProgress() const noexcept
        {
            return shutdownInProgress;
        }
    };
} // namespace service::gui

M module-services/service-gui/messages/GUIMessage.hpp => module-services/service-gui/messages/GUIMessage.hpp +1 -17
@@ 10,26 10,10 @@
namespace sgui
{

    /*
     * @brief Template for all messages that go to gui service
     */
    class GUIMessage : public sys::DataMessage
    {
        bool suspend  = false;
        bool shutdown = false;

      public:
        GUIMessage(MessageType messageType, bool suspend = false, bool shutdown = false)
            : sys::DataMessage(messageType), suspend{suspend}, shutdown{shutdown} {};
        virtual ~GUIMessage(){};
        bool getSuspend()
        {
            return suspend;
        };
        bool getShutdown()
        {
            return shutdown;
        };
        GUIMessage() : sys::DataMessage(MessageType::GUIMessage){};
    };

} /* namespace sgui */

A module-services/service-gui/messages/RenderingFinished.hpp => module-services/service-gui/messages/RenderingFinished.hpp +12 -0
@@ 0,0 1,12 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include "GUIMessage.hpp"

namespace service::gui
{
    class RenderingFinished : public sgui::GUIMessage
    {};
} // namespace service::gui

M module-sys/Service/Service.cpp => module-sys/Service/Service.cpp +15 -2
@@ 21,15 21,26 @@
#include <iosfwd>              // for std
#include <typeinfo>            // for type_info

#if (DEBUG_SERVICE_MESSAGES > 0)
#include <cxxabi.h>
#endif

// this could use Scoped() class from utils to print execution time too
void debug_msg(sys::Service *srvc, sys::DataMessage *&ptr)
{
#if (DEBUG_SERVICE_MESSAGES > 0)

    int status     = -1;
    char *realname = nullptr;
    realname       = abi::__cxa_demangle(typeid(*ptr).name(), 0, 0, &status);

    LOG_DEBUG("Handle message ([%s] -> [%s] (%s) data: %s",
              ptr->sender.c_str(),
              srvc->GetName().c_str(),
              typeid(*ptr).name(),
              realname,
              std::string(*ptr).c_str());

    free(realname);
#else
#endif
}


@@ 86,7 97,6 @@ namespace sys
            /// this is the only place that uses Reponse messages (service manager doesnt...)
            auto ret = msg->Execute(this);
            if (ret == nullptr) {
                LOG_FATAL("NO MESSAGE from: %s msg type: %d", msg->sender.c_str(), static_cast<int>(msg->type));
                ret = std::make_shared<DataMessage>(MessageType::MessageTypeUninitialized);
            }



@@ 110,6 120,9 @@ namespace sys
        debug_msg(this, message);
        if (handler != message_handlers.end()) {
            ret = message_handlers[idx](message, response);
            if (ret != nullptr) {
                ret->type = sys::Message::Type::Response;
            }
        }
        else {
            ret = DataReceivedHandler(message, response);

M module-utils/taglib => module-utils/taglib +1 -1
@@ 1,1 1,1 @@
Subproject commit fa37896f615f3d347a41f0db711e23746774b15f
Subproject commit b2a6e50aedf0cfe1f808eb23dc9f572a848ddffe

M source/MessageType.hpp => source/MessageType.hpp +2 -13
@@ 10,20 10,9 @@ enum class MessageType
    MessageTypeUninitialized = 0,
    // eink messages
    EinkMessage,
    EinkStateRequest,      ///< message is used to pull status of the eink. If eink is ready to display image
    EinkImageData,         ///< message with pointer to the image data for displaying
    EinkDMATransfer,       ///< this message is internally sent from wink service to eink service. This will trigger DMA
                           ///< transfer to Eink's controller. After tranfer gui service wil lbewill be notified
    EinkTemperatureUpdate, ///< message sent from timer handler. Message forces service to update temperature measured
                           ///< by the eink.

    // gui messages
    GUICommands,          ///< list of rendering commands
    GUIFocusInfo,         ///< information about application that gained focus
    GUIDisplayReady,      ///< message that informs gui service that service controlling display device is ready for new
                          ///< buffer data.
    GUIRenderingFinished, ///< Message is sent from the worker when all rendering is finished.

    GUIMessage,
    GUIFocusInfo, ///< information about application that gained focus
    // DB messages
    DBServiceEvents,