~aleteoryx/muditaos

ref: b84eda2e2771cc7d6ca3339f9aef9d5afe566b7d muditaos/module-services/service-gui/ServiceGUI.cpp -rw-r--r-- 10.0 KiB
b84eda2e — Paweł Olejniczak [EGD-2629] Add Phone name window (#887) 5 years ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
// 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 */