Fix for x86_64 build fail
[platform/upstream/connectedhomeip.git] / examples / common / screen-framework / ScreenManager.cpp
1 /*
2  *
3  *    Copyright (c) 2020 Project CHIP Authors
4  *    All rights reserved.
5  *
6  *    Licensed under the Apache License, Version 2.0 (the "License");
7  *    you may not use this file except in compliance with the License.
8  *    You may obtain a copy of the License at
9  *
10  *        http://www.apache.org/licenses/LICENSE-2.0
11  *
12  *    Unless required by applicable law or agreed to in writing, software
13  *    distributed under the License is distributed on an "AS IS" BASIS,
14  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  *    See the License for the specific language governing permissions and
16  *    limitations under the License.
17  */
18
19 /**
20  * @file ScreenManager.cpp
21  *
22  * Simple screen manager.
23  *
24  */
25
26 #include "ScreenManager.h"
27
28 #if CONFIG_HAVE_DISPLAY
29
30 #include <support/CHIPMem.h>
31
32 #include <cassert>
33 #include <vector>
34
35 uint16_t ScreenFontHeight;
36 uint16_t ScreenTitleSafeTop;
37 uint16_t ScreenTitleSafeBottom;
38
39 color_t ScreenNormalColor = { 255, 255, 255 };
40 color_t ScreenFocusColor  = { 128, 128, 255 };
41 color_t ScreenButtonColor = { 64, 64, 64 };
42
43 namespace {
44
45 constexpr int kMainFont   = DEJAVU24_FONT;
46 constexpr int kButtonFont = DEJAVU18_FONT;
47
48 constexpr int kVLEDWidth  = 16;
49 constexpr int kVLEDHeight = 16;
50
51 SemaphoreHandle_t mutex;
52
53 struct Lock
54 {
55     Lock() { xSemaphoreTakeRecursive(mutex, portMAX_DELAY); }
56     ~Lock() { xSemaphoreGive(mutex); }
57 };
58
59 struct VLED
60 {
61     color_t color;
62     color_t color_off;
63     bool on;
64     VLED(color_t color) : color(color), on(false)
65     {
66         color_off = color;
67         color_off.r &= 0x1F;
68         color_off.g &= 0x1F;
69         color_off.b &= 0x1F;
70     }
71 };
72
73 std::vector<VLED> vleds;
74
75 std::vector<Screen *> screens;
76
77 bool focusBack = false;
78
79 int lazyDisplay   = 0;
80 bool dirtyDisplay = false;
81
82 struct LazyDisplay
83 {
84     LazyDisplay() { ++lazyDisplay; }
85     ~LazyDisplay()
86     {
87         if (--lazyDisplay == 0)
88         {
89             if (dirtyDisplay)
90             {
91                 ScreenManager::Display();
92                 dirtyDisplay = false;
93             }
94         }
95     }
96 };
97
98 // Print text centered horizontally at x.
99 void PrintCentered(const char * s, int x, int y)
100 {
101     TFT_print(s, x - (TFT_getStringWidth(s) / 2), y);
102 }
103
104 // Print button text in appropriate location (1 to 3).
105 void DisplayButtonText(int id, const char * s)
106 {
107     tft_fg = ScreenButtonColor;
108     int x  = (DisplayWidth / 2) + (id - 2) * (DisplayWidth * 3 / 10);
109     PrintCentered(s, x, DisplayHeight - (ScreenTitleSafeBottom / 2)); // within ScreenTitleSafeBottom
110 }
111
112 void DisplayVLED(int id)
113 {
114     TFT_fillRect(0, ScreenFontHeight * 3 / 2 + id * (kVLEDHeight + 2), kVLEDWidth, kVLEDHeight,
115                  vleds[id].on ? vleds[id].color : vleds[id].color_off);
116 }
117
118 } // namespace
119
120 namespace ScreenManager {
121
122 void Init()
123 {
124     mutex = xSemaphoreCreateRecursiveMutex();
125
126     // https://github.com/loboris/ESP32_TFT_library/issues/48
127     TFT_setFont(kButtonFont, nullptr);
128     ScreenTitleSafeBottom = TFT_getfontheight() * 2;
129     TFT_setFont(kMainFont, nullptr);
130     ScreenFontHeight   = TFT_getfontheight();
131     ScreenTitleSafeTop = ScreenFontHeight * 5 / 2;
132 }
133
134 void Display()
135 {
136     Lock lock;
137
138     if (lazyDisplay)
139     {
140         dirtyDisplay = true;
141         return;
142     }
143
144     TFT_fillScreen(TFT_BLACK);
145     TFT_setFont(kMainFont, nullptr);
146
147     if (screens.empty())
148     {
149         tft_fg = TFT_RED;
150         PrintCentered("No Screen", DisplayWidth / 2, DisplayHeight / 2);
151         return;
152     }
153
154     if (screens.size() > 1)
155     {
156         tft_fg = focusBack ? ScreenFocusColor : ScreenNormalColor;
157         TFT_print("<", ScreenFontHeight, ScreenFontHeight / 2);
158     }
159
160     std::string title = screens.back()->GetTitle();
161     tft_fg            = ScreenNormalColor;
162     TFT_print(title.c_str(), ScreenTitleSafeTop, ScreenFontHeight / 2); // within ScreenTitleSafeTop
163     TFT_drawRect(ScreenTitleSafeTop, ScreenFontHeight * 3 / 2, TFT_getStringWidth(title.c_str()), 2, ScreenNormalColor);
164
165     TFT_setFont(kButtonFont, nullptr);
166     if (screens.back()->IsFocusable())
167     {
168         DisplayButtonText(1, screens.back()->GetButtonText(1).c_str());
169         DisplayButtonText(2, screens.back()->GetButtonText(2).c_str());
170     }
171     if (focusBack)
172     {
173         DisplayButtonText(3, "Back");
174     }
175     else if (screens.back()->IsFocusable())
176     {
177         DisplayButtonText(3, screens.back()->GetButtonText(3).c_str());
178     }
179     TFT_setFont(kMainFont, nullptr);
180
181     for (int i = 0; i < vleds.size(); ++i)
182     {
183         DisplayVLED(i);
184     }
185
186     screens.back()->Display();
187 }
188
189 void ButtonPressed(int id)
190 {
191     Lock lock;
192     LazyDisplay lazy;
193
194     if (screens.empty())
195     {
196         return;
197     }
198
199     if (focusBack && id == 3)
200     {
201         PopScreen();
202     }
203     else if (screens.back()->IsFocusable())
204     {
205         switch (id)
206         {
207         case 1:
208             focusBack = false;
209             screens.back()->Focus(Screen::FocusType::PREVIOUS);
210             break;
211         case 2:
212             focusBack = false;
213             screens.back()->Focus(Screen::FocusType::NEXT);
214             break;
215         case 3:
216             screens.back()->Action();
217             break;
218         }
219         Display();
220     }
221 }
222
223 void PushScreen(Screen * screen)
224 {
225     Lock lock;
226     LazyDisplay lazy;
227
228     if (!screens.empty())
229     {
230         if (screens.back()->IsFocusable())
231         {
232             screens.back()->Focus(Screen::FocusType::BLUR);
233         }
234         screens.back()->Exit(false);
235     }
236
237     screen->Enter(true);       // screen is not top when enter/pushed
238     screens.push_back(screen); // screen is pushed immediately after first enter
239
240     focusBack = false;
241
242     if (screens.back()->IsFocusable())
243     {
244         screens.back()->Focus(Screen::FocusType::NEXT);
245     }
246     else
247     {
248         focusBack = true;
249     }
250
251     Display();
252 }
253
254 void PopScreen()
255 {
256     Lock lock;
257     LazyDisplay lazy;
258
259     if (screens.empty())
260     {
261         return;
262     }
263
264     Screen * screen = screens.back();
265     screens.pop_back(); // screen is popped immediately before last exit
266     screen->Exit(true); // screen is not top when exit/popped
267     chip::Platform::Delete(screen);
268
269     focusBack = false;
270
271     if (!screens.empty())
272     {
273         screens.back()->Enter(false);
274         if (screens.back()->IsFocusable())
275         {
276             screens.back()->Focus(Screen::FocusType::UNBLUR);
277         }
278         else
279         {
280             focusBack = true;
281         }
282     }
283
284     Display();
285 }
286
287 void FocusBack()
288 {
289     Lock lock;
290     if (screens.size() > 1)
291     {
292         focusBack = true;
293         if (screens.back()->IsFocusable())
294         {
295             screens.back()->Focus(Screen::FocusType::NONE);
296         }
297     }
298     else
299     {
300         focusBack = false;
301     }
302 }
303
304 int AddVLED(color_t color)
305 {
306     Lock lock;
307     int id = vleds.size();
308     vleds.emplace_back(color);
309     DisplayVLED(id);
310     return id;
311 }
312
313 void SetVLED(int id, bool on)
314 {
315     Lock lock;
316     if (vleds[id].on == on)
317     {
318         return;
319     }
320
321     vleds[id].on = on;
322     DisplayVLED(id);
323     WakeDisplay();
324 }
325
326 void ToggleVLED(int id)
327 {
328     Lock lock;
329     vleds[id].on = !vleds[id].on;
330     DisplayVLED(id);
331     WakeDisplay();
332 }
333
334 } // namespace ScreenManager
335
336 #endif // CONFIG_HAVE_DISPLAY