ScreenManager.cpp 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336
  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. * @file ScreenManager.cpp
  20. *
  21. * Simple screen manager.
  22. *
  23. */
  24. #include "ScreenManager.h"
  25. #if CONFIG_HAVE_DISPLAY
  26. #include <lib/support/CHIPMem.h>
  27. #include <cassert>
  28. #include <vector>
  29. uint16_t ScreenFontHeight;
  30. uint16_t ScreenTitleSafeTop;
  31. uint16_t ScreenTitleSafeBottom;
  32. color_t ScreenNormalColor = { 255, 255, 255 };
  33. color_t ScreenFocusColor = { 128, 128, 255 };
  34. color_t ScreenButtonColor = { 64, 64, 64 };
  35. namespace {
  36. constexpr int kMainFont = DEJAVU24_FONT;
  37. constexpr int kButtonFont = DEJAVU18_FONT;
  38. constexpr int kVLEDWidth = 16;
  39. constexpr int kVLEDHeight = 16;
  40. SemaphoreHandle_t mutex;
  41. struct Lock
  42. {
  43. Lock() { xSemaphoreTakeRecursive(mutex, portMAX_DELAY); }
  44. ~Lock() { xSemaphoreGiveRecursive(mutex); }
  45. };
  46. struct VLED
  47. {
  48. color_t color;
  49. color_t color_off;
  50. bool on;
  51. VLED(color_t color) : color(color), on(false)
  52. {
  53. color_off = color;
  54. color_off.r &= 0x1F;
  55. color_off.g &= 0x1F;
  56. color_off.b &= 0x1F;
  57. }
  58. };
  59. std::vector<VLED> vleds;
  60. std::vector<Screen *> screens;
  61. bool focusBack = false;
  62. int lazyDisplay = 0;
  63. bool dirtyDisplay = false;
  64. struct LazyDisplay
  65. {
  66. LazyDisplay() { ++lazyDisplay; }
  67. ~LazyDisplay()
  68. {
  69. if (--lazyDisplay == 0)
  70. {
  71. if (dirtyDisplay)
  72. {
  73. ScreenManager::Display();
  74. dirtyDisplay = false;
  75. }
  76. }
  77. }
  78. };
  79. // Print text centered horizontally at x.
  80. void PrintCentered(const char * s, int x, int y)
  81. {
  82. TFT_print(s, x - (TFT_getStringWidth(s) / 2), y);
  83. }
  84. // Print button text in appropriate location (1 to 3).
  85. void DisplayButtonText(int id, const char * s)
  86. {
  87. tft_fg = ScreenButtonColor;
  88. int x = (DisplayWidth / 2) + (id - 2) * (DisplayWidth * 3 / 10);
  89. PrintCentered(s, x, DisplayHeight - (ScreenTitleSafeBottom / 2)); // within ScreenTitleSafeBottom
  90. }
  91. void DisplayVLED(int id)
  92. {
  93. TFT_fillRect(0, ScreenFontHeight * 3 / 2 + id * (kVLEDHeight + 2), kVLEDWidth, kVLEDHeight,
  94. vleds[id].on ? vleds[id].color : vleds[id].color_off);
  95. }
  96. } // namespace
  97. namespace ScreenManager {
  98. void Init()
  99. {
  100. mutex = xSemaphoreCreateRecursiveMutex();
  101. // https://github.com/loboris/ESP32_TFT_library/issues/48
  102. TFT_setFont(kButtonFont, nullptr);
  103. ScreenTitleSafeBottom = TFT_getfontheight() * 2;
  104. TFT_setFont(kMainFont, nullptr);
  105. ScreenFontHeight = TFT_getfontheight();
  106. ScreenTitleSafeTop = ScreenFontHeight * 5 / 2;
  107. }
  108. void Display()
  109. {
  110. Lock lock;
  111. if (lazyDisplay)
  112. {
  113. dirtyDisplay = true;
  114. return;
  115. }
  116. TFT_fillScreen(TFT_BLACK);
  117. TFT_setFont(kMainFont, nullptr);
  118. if (screens.empty())
  119. {
  120. tft_fg = TFT_RED;
  121. PrintCentered("No Screen", DisplayWidth / 2, DisplayHeight / 2);
  122. return;
  123. }
  124. if (screens.size() > 1)
  125. {
  126. tft_fg = focusBack ? ScreenFocusColor : ScreenNormalColor;
  127. TFT_print("<", ScreenFontHeight, ScreenFontHeight / 2);
  128. }
  129. std::string title = screens.back()->GetTitle();
  130. tft_fg = ScreenNormalColor;
  131. TFT_print(title.c_str(), ScreenTitleSafeTop, ScreenFontHeight / 2); // within ScreenTitleSafeTop
  132. TFT_drawRect(ScreenTitleSafeTop, ScreenFontHeight * 3 / 2, TFT_getStringWidth(title.c_str()), 2, ScreenNormalColor);
  133. TFT_setFont(kButtonFont, nullptr);
  134. if (screens.back()->IsFocusable())
  135. {
  136. DisplayButtonText(1, screens.back()->GetButtonText(1).c_str());
  137. DisplayButtonText(2, screens.back()->GetButtonText(2).c_str());
  138. }
  139. if (focusBack)
  140. {
  141. DisplayButtonText(3, "Back");
  142. }
  143. else if (screens.back()->IsFocusable())
  144. {
  145. DisplayButtonText(3, screens.back()->GetButtonText(3).c_str());
  146. }
  147. TFT_setFont(kMainFont, nullptr);
  148. for (int i = 0; i < vleds.size(); ++i)
  149. {
  150. DisplayVLED(i);
  151. }
  152. screens.back()->Display();
  153. }
  154. void ButtonPressed(int id)
  155. {
  156. Lock lock;
  157. LazyDisplay lazy;
  158. if (screens.empty())
  159. {
  160. return;
  161. }
  162. if (focusBack && id == 3)
  163. {
  164. PopScreen();
  165. }
  166. else if (screens.back()->IsFocusable())
  167. {
  168. switch (id)
  169. {
  170. case 1:
  171. focusBack = false;
  172. screens.back()->Focus(Screen::FocusType::PREVIOUS);
  173. break;
  174. case 2:
  175. focusBack = false;
  176. screens.back()->Focus(Screen::FocusType::NEXT);
  177. break;
  178. case 3:
  179. screens.back()->Action();
  180. break;
  181. }
  182. Display();
  183. }
  184. }
  185. void PushScreen(Screen * screen)
  186. {
  187. Lock lock;
  188. LazyDisplay lazy;
  189. if (!screens.empty())
  190. {
  191. if (screens.back()->IsFocusable())
  192. {
  193. screens.back()->Focus(Screen::FocusType::BLUR);
  194. }
  195. screens.back()->Exit(false);
  196. }
  197. screen->Enter(true); // screen is not top when enter/pushed
  198. screens.push_back(screen); // screen is pushed immediately after first enter
  199. focusBack = false;
  200. if (screens.back()->IsFocusable())
  201. {
  202. screens.back()->Focus(Screen::FocusType::NEXT);
  203. }
  204. else
  205. {
  206. focusBack = true;
  207. }
  208. Display();
  209. }
  210. void PopScreen()
  211. {
  212. Lock lock;
  213. LazyDisplay lazy;
  214. if (screens.empty())
  215. {
  216. return;
  217. }
  218. Screen * screen = screens.back();
  219. screens.pop_back(); // screen is popped immediately before last exit
  220. screen->Exit(true); // screen is not top when exit/popped
  221. chip::Platform::Delete(screen);
  222. focusBack = false;
  223. if (!screens.empty())
  224. {
  225. screens.back()->Enter(false);
  226. if (screens.back()->IsFocusable())
  227. {
  228. screens.back()->Focus(Screen::FocusType::UNBLUR);
  229. }
  230. else
  231. {
  232. focusBack = true;
  233. }
  234. }
  235. Display();
  236. }
  237. void FocusBack()
  238. {
  239. Lock lock;
  240. if (screens.size() > 1)
  241. {
  242. focusBack = true;
  243. if (screens.back()->IsFocusable())
  244. {
  245. screens.back()->Focus(Screen::FocusType::NONE);
  246. }
  247. }
  248. else
  249. {
  250. focusBack = false;
  251. }
  252. }
  253. int AddVLED(color_t color)
  254. {
  255. Lock lock;
  256. int id = vleds.size();
  257. vleds.emplace_back(color);
  258. DisplayVLED(id);
  259. return id;
  260. }
  261. void SetVLED(int id, bool on)
  262. {
  263. Lock lock;
  264. if (vleds[id].on == on)
  265. {
  266. return;
  267. }
  268. vleds[id].on = on;
  269. DisplayVLED(id);
  270. WakeDisplay();
  271. }
  272. void ToggleVLED(int id)
  273. {
  274. Lock lock;
  275. vleds[id].on = !vleds[id].on;
  276. DisplayVLED(id);
  277. WakeDisplay();
  278. }
  279. } // namespace ScreenManager
  280. #endif // CONFIG_HAVE_DISPLAY