| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #include "fpp-pch.h" | ||
| 2 | #include <memory> | ||
| 3 | |||
| 4 | #include <fstream> | ||
| 5 | |||
| 6 | #include "OLEDPages.h" | ||
| 7 | #include "../common.h" | ||
| 8 | |||
| 9 | static std::unique_ptr<DisplayDriver> displayDriver{ nullptr }; | ||
| 10 | |||
| 11 | OLEDPage::OLEDType OLEDPage::oledType = OLEDPage::OLEDType::SINGLE_COLOR; | ||
| 12 | bool OLEDPage::oledFlipped = false; | ||
| 13 | OLEDPage* OLEDPage::currentPage = nullptr; | ||
| 14 | bool OLEDPage::oledForcedOff = false; | ||
| 15 | bool OLEDPage::has4DirectionControls = false; | ||
| 16 | |||
| 17 | int OLEDPage::_capeImageWidth = 0; | ||
| 18 | int OLEDPage::_capeImageHeight = 0; | ||
| 19 | std::vector<uint8_t> OLEDPage::_capeImage; | ||
| 20 | |||
| 21 | static int DISPLAY_I2CBUS = 0; | ||
| 22 | |||
| 23 | 16 | bool OLEDPage::InitializeDisplay(int ledType) { | |
| 24 |
2/2✓ Branch 0 taken 4 times.
✓ Branch 1 taken 12 times.
|
16 | if (ledType == 0) { |
| 25 | 4 | SetOLEDType(OLEDPage::OLEDType::NONE); | |
| 26 | 4 | return false; | |
| 27 | } | ||
| 28 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
12 | if (ledType <= 10 || ledType >= 32) { |
| 29 |
1/2✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
|
12 | displayDriver = std::make_unique<SSD1306DisplayDriver>(ledType); |
| 30 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
|
12 | if (!displayDriver->initialize(DISPLAY_I2CBUS)) { |
| 31 | ✗ | displayDriver.reset(nullptr); | |
| 32 | ✗ | SetOLEDType(OLEDPage::OLEDType::NONE); | |
| 33 | ✗ | return false; | |
| 34 | } | ||
| 35 | |||
| 36 |
2/4✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 12 times.
|
12 | if (ledType == 3 || ledType == 4) { |
| 37 | ✗ | SetOLEDType(OLEDPage::OLEDType::SMALL); | |
| 38 |
2/4✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 12 times.
|
12 | } else if (ledType == 7 || ledType == 8) { |
| 39 | ✗ | SetOLEDType(OLEDPage::OLEDType::TWO_COLOR); | |
| 40 | } else { | ||
| 41 | 12 | SetOLEDType(OLEDPage::OLEDType::SINGLE_COLOR); | |
| 42 | } | ||
| 43 |
6/12✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 12 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 12 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 12 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 12 times.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✓ Branch 11 taken 12 times.
|
12 | if (ledType == 2 || ledType == 4 || ledType == 6 || ledType == 8 || ledType == 10 || ledType == 35) { |
| 44 | ✗ | SetOLEDOrientationFlipped(true); | |
| 45 | } | ||
| 46 | |||
| 47 | 12 | return true; | |
| 48 | ✗ | } else if (ledType <= 20) { | |
| 49 | ✗ | displayDriver = std::make_unique<I2C1602_2004_DisplayDriver>(ledType); | |
| 50 | ✗ | if (!displayDriver->initialize(DISPLAY_I2CBUS)) { | |
| 51 | ✗ | displayDriver.reset(nullptr); | |
| 52 | ✗ | SetOLEDType(OLEDPage::OLEDType::NONE); | |
| 53 | ✗ | return false; | |
| 54 | } | ||
| 55 | ✗ | SetOLEDType(OLEDPage::OLEDType::TEXT_ONLY); | |
| 56 | ✗ | return true; | |
| 57 | } | ||
| 58 | ✗ | return false; | |
| 59 | } | ||
| 60 | |||
| 61 | 12 | int OLEDPage::GetI2CBus() { | |
| 62 | 12 | return DISPLAY_I2CBUS; | |
| 63 | } | ||
| 64 | |||
| 65 | 16 | void OLEDPage::SetOLEDType(OLEDType tp) { | |
| 66 | 16 | oledType = tp; | |
| 67 | 16 | } | |
| 68 | |||
| 69 | 12 | void OLEDPage::displayBootingNotice(const std::string& msg) { | |
| 70 |
1/2✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
|
12 | if (displayDriver) { |
| 71 |
3/10✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 12 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 12 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
|
12 | std::string m = msg.empty() ? "FPP Booting..." : msg; |
| 72 |
1/2✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
|
12 | readCapeImage(); |
| 73 |
1/2✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
|
12 | displayDriver->clearDisplay(); |
| 74 |
1/2✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
|
12 | displayDriver->printString(0, 0, m, true); |
| 75 |
1/2✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
|
12 | drawBitmap(0, 20, &_capeImage[0], _capeImageWidth, _capeImageHeight); |
| 76 |
1/2✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
|
12 | displayDriver->flushDisplay(); |
| 77 | 12 | } | |
| 78 | 12 | } | |
| 79 | |||
| 80 | 59 | void OLEDPage::flushDisplay() { | |
| 81 |
1/2✓ Branch 0 taken 59 times.
✗ Branch 1 not taken.
|
59 | if (displayDriver) { |
| 82 | 59 | displayDriver->flushDisplay(); | |
| 83 | } | ||
| 84 | 59 | } | |
| 85 | 59 | void OLEDPage::clearDisplay() { | |
| 86 |
1/2✓ Branch 0 taken 59 times.
✗ Branch 1 not taken.
|
59 | if (displayDriver) { |
| 87 | 59 | displayDriver->clearDisplay(); | |
| 88 | } | ||
| 89 | 59 | } | |
| 90 | 7 | void OLEDPage::fillTriangle(short x0, short y0, short x1, short y1, short x2, short y2, bool white) { | |
| 91 |
1/2✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
|
7 | if (displayDriver) { |
| 92 | 7 | displayDriver->fillTriangle(x0, y0, x1, y1, x2, y2, white); | |
| 93 | } | ||
| 94 | 7 | } | |
| 95 | 52 | void OLEDPage::drawLine(short x0, short y0, short x1, short y1, bool white) { | |
| 96 |
1/2✓ Branch 0 taken 52 times.
✗ Branch 1 not taken.
|
52 | if (displayDriver) { |
| 97 | 52 | displayDriver->drawLine(x0, y0, x1, y1, white); | |
| 98 | } | ||
| 99 | 52 | } | |
| 100 | 56 | void OLEDPage::drawBitmap(short x, short y, const unsigned char bitmap[], short w, short h, bool white) { | |
| 101 |
1/2✓ Branch 0 taken 56 times.
✗ Branch 1 not taken.
|
56 | if (displayDriver) { |
| 102 | 56 | displayDriver->drawBitmap(x, y, bitmap, w, h, white); | |
| 103 | } | ||
| 104 | 56 | } | |
| 105 | 7 | void OLEDPage::drawRect(short x0, short y0, short x1, short y1, bool white) { | |
| 106 |
1/2✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
|
7 | if (displayDriver) { |
| 107 | 7 | displayDriver->drawRect(x0, y0, x1, y1, white); | |
| 108 | } | ||
| 109 | 7 | } | |
| 110 | 14 | void OLEDPage::fillRect(short x0, short y0, short x1, short y1, bool white) { | |
| 111 |
1/2✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
|
14 | if (displayDriver) { |
| 112 | 14 | displayDriver->fillRect(x0, y0, x1, y1, white); | |
| 113 | } | ||
| 114 | 14 | } | |
| 115 | |||
| 116 | 139 | void OLEDPage::printString(int x, int y, const std::string& str, bool white) { | |
| 117 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 139 times.
|
139 | if (oledForcedOff) |
| 118 | ✗ | return; | |
| 119 |
1/2✓ Branch 0 taken 139 times.
✗ Branch 1 not taken.
|
139 | if (displayDriver) { |
| 120 | 139 | displayDriver->printString(x, y, str, white); | |
| 121 | } | ||
| 122 | } | ||
| 123 | 7 | void OLEDPage::printStringCentered(int y, const std::string& str, bool white) { | |
| 124 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
|
7 | if (oledForcedOff) |
| 125 | ✗ | return; | |
| 126 |
1/2✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
|
7 | if (displayDriver) { |
| 127 | 7 | displayDriver->printStringCentered(y, str, white); | |
| 128 | } | ||
| 129 | } | ||
| 130 | 156 | int OLEDPage::GetLEDDisplayWidth() { | |
| 131 |
1/2✓ Branch 0 taken 156 times.
✗ Branch 1 not taken.
|
156 | if (displayDriver) { |
| 132 | 156 | return displayDriver->getWidth(); | |
| 133 | } | ||
| 134 | ✗ | return 128; | |
| 135 | } | ||
| 136 | 172 | int OLEDPage::GetLEDDisplayHeight() { | |
| 137 |
1/2✓ Branch 0 taken 172 times.
✗ Branch 1 not taken.
|
172 | if (displayDriver) { |
| 138 | 172 | return displayDriver->getHeight(); | |
| 139 | } | ||
| 140 | ✗ | return 32; | |
| 141 | } | ||
| 142 | 12 | void OLEDPage::displayOff() { | |
| 143 |
1/2✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
|
12 | if (displayDriver) { |
| 144 | 12 | return displayDriver->displayOff(); | |
| 145 | } | ||
| 146 | } | ||
| 147 | ✗ | int OLEDPage::getMinimumRefresh() { | |
| 148 | ✗ | if (displayDriver) { | |
| 149 | ✗ | return displayDriver->getMinimumRefresh(); | |
| 150 | } | ||
| 151 | ✗ | return 1000; | |
| 152 | } | ||
| 153 | |||
| 154 | 24 | OLEDPage::OLEDPage() : | |
| 155 | 24 | autoDeleteOnHide(false) { | |
| 156 | 24 | } | |
| 157 | |||
| 158 | 17 | void OLEDPage::SetCurrentPage(OLEDPage* p) { | |
| 159 |
2/2✓ Branch 0 taken 5 times.
✓ Branch 1 taken 12 times.
|
17 | if (currentPage) { |
| 160 | 5 | currentPage->hiding(); | |
| 161 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
|
5 | if (currentPage->autoDeleteOnHide) { |
| 162 | ✗ | delete currentPage; | |
| 163 | } | ||
| 164 | } | ||
| 165 | 17 | currentPage = p; | |
| 166 |
1/2✓ Branch 0 taken 17 times.
✗ Branch 1 not taken.
|
17 | if (currentPage) { |
| 167 | 17 | currentPage->displaying(); | |
| 168 | } | ||
| 169 | 17 | } | |
| 170 | |||
| 171 | // trim from start (in place) | ||
| 172 | 14320 | static inline void ltrim(std::string& s) { | |
| 173 |
2/4✓ Branch 0 taken 14320 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 14320 times.
✗ Branch 3 not taken.
|
14320 | s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](int ch) { |
| 174 | 26380 | return !std::isspace(ch); | |
| 175 | })); | ||
| 176 | 14320 | } | |
| 177 | // trim from end (in place) | ||
| 178 | 14320 | static inline void rtrim(std::string& s) { | |
| 179 |
2/4✓ Branch 0 taken 14320 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 14320 times.
✗ Branch 3 not taken.
|
42960 | s.erase(std::find_if(s.rbegin(), s.rend(), [](int ch) { |
| 180 | 14296 | return !std::isspace(ch); | |
| 181 | 14320 | }).base(), | |
| 182 | 14320 | s.end()); | |
| 183 | 14320 | } | |
| 184 | // trim from both ends (in place) | ||
| 185 | 14320 | static inline void trim(std::string& s) { | |
| 186 | 14320 | ltrim(s); | |
| 187 | 14320 | rtrim(s); | |
| 188 | 14320 | } | |
| 189 | 1096 | static std::vector<std::string> splitAndTrim(const std::string& s, char seperator) { | |
| 190 | 1096 | std::vector<std::string> output; | |
| 191 | 1096 | std::string::size_type prev_pos = 0, pos = 0; | |
| 192 |
2/2✓ Branch 0 taken 12128 times.
✓ Branch 1 taken 1096 times.
|
13224 | while ((pos = s.find(seperator, pos)) != std::string::npos) { |
| 193 |
1/2✓ Branch 0 taken 12128 times.
✗ Branch 1 not taken.
|
12128 | std::string substring(s.substr(prev_pos, pos - prev_pos)); |
| 194 |
1/2✓ Branch 0 taken 12128 times.
✗ Branch 1 not taken.
|
12128 | trim(substring); |
| 195 |
2/4✓ Branch 0 taken 12128 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 12128 times.
✗ Branch 3 not taken.
|
12128 | if (substring != "") { |
| 196 |
1/2✓ Branch 0 taken 12128 times.
✗ Branch 1 not taken.
|
12128 | output.push_back(substring); |
| 197 | } | ||
| 198 | 12128 | prev_pos = ++pos; | |
| 199 | 12128 | } | |
| 200 |
1/2✓ Branch 0 taken 1096 times.
✗ Branch 1 not taken.
|
1096 | std::string substring = s.substr(prev_pos, pos - prev_pos); |
| 201 |
1/2✓ Branch 0 taken 1096 times.
✗ Branch 1 not taken.
|
1096 | trim(substring); |
| 202 |
3/4✓ Branch 0 taken 1096 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 72 times.
✓ Branch 3 taken 1024 times.
|
1096 | if (substring != "") { |
| 203 |
1/2✓ Branch 0 taken 72 times.
✗ Branch 1 not taken.
|
72 | output.push_back(substring); // Last word |
| 204 | } | ||
| 205 | 1096 | return output; | |
| 206 | 1096 | } | |
| 207 | static const unsigned char reverselookup[16] = { | ||
| 208 | 0x0, | ||
| 209 | 0x8, | ||
| 210 | 0x4, | ||
| 211 | 0xc, | ||
| 212 | 0x2, | ||
| 213 | 0xa, | ||
| 214 | 0x6, | ||
| 215 | 0xe, | ||
| 216 | 0x1, | ||
| 217 | 0x9, | ||
| 218 | 0x5, | ||
| 219 | 0xd, | ||
| 220 | 0x3, | ||
| 221 | 0xb, | ||
| 222 | 0x7, | ||
| 223 | 0xf, | ||
| 224 | }; | ||
| 225 | |||
| 226 | 12012 | static inline uint8_t reverse(uint8_t n) { | |
| 227 | // Reverse the top and bottom nibble then swap them. | ||
| 228 | 12012 | return (reverselookup[n & 0b1111] << 4) | reverselookup[n >> 4]; | |
| 229 | } | ||
| 230 | |||
| 231 | 24 | void OLEDPage::readCapeImage() { | |
| 232 | // The image comes from the cape (its EEPROM, via fppcapedetect), so it is | ||
| 233 | // parsed as untrusted: a short "#define" line indexed past the split, a | ||
| 234 | // byte that wasn't hex threw out of stoi and ended fppoled, and nothing | ||
| 235 | // checked the bytes covered width x height before drawBitmap() read them. | ||
| 236 | // Anything off leaves no image. | ||
| 237 |
2/6✗ Branch 0 not taken.
✓ Branch 1 taken 24 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 24 times.
✗ Branch 5 not taken.
|
24 | if (FileExists("/home/fpp/media/tmp/cape-image.xbm") || FileExists("/var/tmp/cape-image.xbm")) { |
| 238 |
3/6✓ Branch 0 taken 24 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 24 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 24 times.
✗ Branch 5 not taken.
|
48 | std::string fn = FileExists("/home/fpp/media/tmp/cape-image.xbm") ? "/home/fpp/media/tmp/cape-image.xbm" : "/var/tmp/cape-image.xbm"; |
| 239 |
1/2✓ Branch 0 taken 24 times.
✗ Branch 1 not taken.
|
24 | std::ifstream file(fn); |
| 240 |
1/2✓ Branch 0 taken 24 times.
✗ Branch 1 not taken.
|
24 | if (file.is_open()) { |
| 241 | 24 | std::string line; | |
| 242 | 24 | bool readingBytes = false; | |
| 243 | 24 | bool ok = true; | |
| 244 | 24 | int width = 0; | |
| 245 | 24 | int height = 0; | |
| 246 | 24 | std::vector<uint8_t> image; | |
| 247 |
8/10✓ Branch 0 taken 1116 times.
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 1116 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1116 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 1096 times.
✓ Branch 7 taken 20 times.
✓ Branch 8 taken 1096 times.
✓ Branch 9 taken 24 times.
|
1120 | while (ok && std::getline(file, line)) { |
| 248 |
1/2✓ Branch 0 taken 1096 times.
✗ Branch 1 not taken.
|
1096 | trim(line); |
| 249 |
2/2✓ Branch 0 taken 72 times.
✓ Branch 1 taken 1024 times.
|
1096 | if (!readingBytes) { |
| 250 |
2/2✓ Branch 0 taken 24 times.
✓ Branch 1 taken 48 times.
|
72 | if (line.find("_width") != std::string::npos) { |
| 251 |
1/2✓ Branch 0 taken 24 times.
✗ Branch 1 not taken.
|
24 | std::vector<std::string> v = splitAndTrim(line, ' '); |
| 252 |
1/2✓ Branch 0 taken 24 times.
✗ Branch 1 not taken.
|
24 | width = v.size() > 2 ? std::atoi(v[2].c_str()) : 0; |
| 253 |
2/2✓ Branch 0 taken 24 times.
✓ Branch 1 taken 24 times.
|
72 | } else if (line.find("_height") != std::string::npos) { |
| 254 |
1/2✓ Branch 0 taken 24 times.
✗ Branch 1 not taken.
|
24 | std::vector<std::string> v = splitAndTrim(line, ' '); |
| 255 |
1/2✓ Branch 0 taken 24 times.
✗ Branch 1 not taken.
|
24 | height = v.size() > 2 ? std::atoi(v[2].c_str()) : 0; |
| 256 |
1/2✓ Branch 0 taken 24 times.
✗ Branch 1 not taken.
|
48 | } else if (line.find("{") != std::string::npos) { |
| 257 | 24 | readingBytes = true; | |
| 258 |
1/2✓ Branch 0 taken 24 times.
✗ Branch 1 not taken.
|
24 | line = line.substr(line.find("{") + 1); |
| 259 | } | ||
| 260 | } | ||
| 261 |
2/2✓ Branch 0 taken 1048 times.
✓ Branch 1 taken 48 times.
|
1096 | if (readingBytes) { |
| 262 |
1/2✓ Branch 0 taken 1048 times.
✗ Branch 1 not taken.
|
1048 | std::vector<std::string> v = splitAndTrim(line, ','); |
| 263 |
2/2✓ Branch 0 taken 12036 times.
✓ Branch 1 taken 1024 times.
|
13060 | for (auto& s : v) { |
| 264 | // The closing brace can share a token with the last | ||
| 265 | // byte ("0x00 };", no trailing comma): keep that byte. | ||
| 266 | 12036 | bool last = false; | |
| 267 | 12036 | size_t brace = s.find("}"); | |
| 268 |
2/2✓ Branch 0 taken 20 times.
✓ Branch 1 taken 12016 times.
|
12036 | if (brace != std::string::npos) { |
| 269 | 20 | readingBytes = false; | |
| 270 | 20 | last = true; | |
| 271 |
1/2✓ Branch 0 taken 20 times.
✗ Branch 1 not taken.
|
20 | s.erase(brace); |
| 272 |
2/6✗ Branch 0 not taken.
✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 20 times.
|
20 | while (!s.empty() && isspace((unsigned char)s.back())) { |
| 273 | ✗ | s.pop_back(); | |
| 274 | } | ||
| 275 | } | ||
| 276 |
2/2✓ Branch 0 taken 20 times.
✓ Branch 1 taken 12016 times.
|
12036 | if (s.empty()) { |
| 277 |
1/2✓ Branch 0 taken 20 times.
✗ Branch 1 not taken.
|
20 | if (last) { |
| 278 | 24 | break; | |
| 279 | } | ||
| 280 | ✗ | continue; | |
| 281 | } | ||
| 282 | 12016 | char* end = nullptr; | |
| 283 | 12016 | long i = strtol(s.c_str(), &end, 16); | |
| 284 |
8/12✓ Branch 0 taken 12012 times.
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 12012 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 12012 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 12012 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 12012 times.
✓ Branch 10 taken 4 times.
✓ Branch 11 taken 12012 times.
|
12016 | if (end == s.c_str() || *end || i < 0 || i > 255 || image.size() >= 65536) { |
| 285 | 4 | ok = false; | |
| 286 | 4 | break; | |
| 287 | } | ||
| 288 | 12012 | uint8_t t8 = (uint8_t)i; | |
| 289 | 12012 | t8 = ~t8; | |
| 290 | 12012 | t8 = reverse(t8); | |
| 291 |
1/2✓ Branch 0 taken 12012 times.
✗ Branch 1 not taken.
|
12012 | image.push_back((uint8_t)t8); |
| 292 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 12012 times.
|
12012 | if (last) { |
| 293 | ✗ | break; | |
| 294 | } | ||
| 295 | } | ||
| 296 | 1048 | } | |
| 297 | } | ||
| 298 |
1/2✓ Branch 0 taken 24 times.
✗ Branch 1 not taken.
|
24 | file.close(); |
| 299 |
8/12✓ Branch 0 taken 20 times.
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 20 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 20 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 20 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 20 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 20 times.
✓ Branch 11 taken 4 times.
|
44 | if (ok && width > 0 && width <= 256 && height > 0 && height <= 256 && |
| 300 |
1/2✓ Branch 0 taken 20 times.
✗ Branch 1 not taken.
|
20 | image.size() >= (size_t)((width + 7) / 8) * height) { |
| 301 | 20 | _capeImageWidth = width; | |
| 302 | 20 | _capeImageHeight = height; | |
| 303 | 20 | _capeImage = std::move(image); | |
| 304 | } | ||
| 305 | 24 | } | |
| 306 | 24 | } | |
| 307 | 24 | } | |
| 308 | |||
| 309 | 12 | TitledOLEDPage::TitledOLEDPage(const std::string& t) : | |
| 310 |
1/2✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
|
12 | title(t) { |
| 311 |
2/4✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 12 times.
|
12 | numRows = GetLEDDisplayHeight() == 128 ? 11 : 5; |
| 312 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
12 | if (oledType == OLEDPage::OLEDType::TWO_COLOR && oledFlipped) { |
| 313 | ✗ | numRows = 4; | |
| 314 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
|
12 | } else if (oledType == OLEDPage::OLEDType::SMALL) { |
| 315 | ✗ | numRows = 3; | |
| 316 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
|
12 | } else if (oledType == OLEDPage::OLEDType::TEXT_ONLY) { |
| 317 | ✗ | numRows = GetLEDDisplayHeight() / 8 - 1; | |
| 318 | } | ||
| 319 | 12 | } | |
| 320 | 7 | int TitledOLEDPage::displayTitle() { | |
| 321 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
|
7 | if (oledForcedOff) |
| 322 | ✗ | return 0; | |
| 323 | |||
| 324 | 7 | int startY = 0; | |
| 325 | 7 | int maxY = 63; | |
| 326 |
1/6✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 7 times.
|
7 | switch (numRows) { |
| 327 | ✗ | case 4: | |
| 328 | ✗ | maxY = 47; | |
| 329 | ✗ | break; | |
| 330 | ✗ | case 3: | |
| 331 | ✗ | maxY = 31; | |
| 332 | ✗ | break; | |
| 333 | ✗ | case 2: | |
| 334 | ✗ | maxY = 23; | |
| 335 | ✗ | break; | |
| 336 | ✗ | case 1: | |
| 337 | ✗ | maxY = 15; | |
| 338 | ✗ | break; | |
| 339 | ✗ | case 11: | |
| 340 | ✗ | maxY = 127; | |
| 341 | ✗ | break; | |
| 342 | } | ||
| 343 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
7 | if (oledType == OLEDPage::OLEDType::TWO_COLOR && !oledFlipped) { |
| 344 | ✗ | if (displayDriver && displayDriver->setFont("Roboto_Medium_14")) { | |
| 345 | ✗ | printStringCentered(startY, title); | |
| 346 | ✗ | displayDriver->setFont(""); | |
| 347 | } else { | ||
| 348 | ✗ | printStringCentered(startY + 4, title); | |
| 349 | } | ||
| 350 | ✗ | startY += 16; | |
| 351 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
|
7 | } else if (oledType == OLEDPage::OLEDType::TEXT_ONLY) { |
| 352 | ✗ | int mw = GetLEDDisplayWidth() / 6; | |
| 353 | ✗ | mw -= title.length(); | |
| 354 | ✗ | std::string s = title; | |
| 355 | ✗ | if (mw > 4) { | |
| 356 | ✗ | s = " " + s + " "; | |
| 357 | ✗ | mw -= 2; | |
| 358 | } | ||
| 359 | ✗ | for (int x = 0; x < mw; x += 2) { | |
| 360 | ✗ | if (x != (mw - 1)) { | |
| 361 | ✗ | s = "=" + s + "="; | |
| 362 | } else { | ||
| 363 | ✗ | s += "="; | |
| 364 | } | ||
| 365 | } | ||
| 366 | ✗ | printString(0, startY, s); | |
| 367 | ✗ | startY += 8; | |
| 368 | ✗ | } else { | |
| 369 | 7 | fillRect(0, 0, 128, 8); | |
| 370 | 7 | drawRect(0, 0, 128, maxY + 1); | |
| 371 | 7 | printStringCentered(startY, title, false); | |
| 372 |
1/2✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
|
7 | if (maxY > 50) { |
| 373 | 7 | startY += 12; | |
| 374 | ✗ | } else if (maxY > 32) { | |
| 375 | ✗ | startY += 11; | |
| 376 | } else { | ||
| 377 | ✗ | startY += 8; | |
| 378 | } | ||
| 379 | } | ||
| 380 | 7 | return startY; | |
| 381 | } | ||
| 382 | |||
| 383 | ✗ | PromptOLEDPage::PromptOLEDPage(const std::string& t, const std::string& m1, const std::string& m2, | |
| 384 | ✗ | const std::vector<std::string>& i) : | |
| 385 | TitledOLEDPage(t), | ||
| 386 | ✗ | msg1(m1), | |
| 387 | ✗ | msg2(m2), | |
| 388 | ✗ | items(i), | |
| 389 | ✗ | curSelected(0), | |
| 390 | ✗ | itemSelectedCallback(nullptr) { | |
| 391 | ✗ | } | |
| 392 | ✗ | PromptOLEDPage::PromptOLEDPage(const std::string& t, const std::string& m1, const std::string& m2, | |
| 393 | const std::vector<std::string>& i, | ||
| 394 | ✗ | const std::function<void(const std::string&)>& cb) : | |
| 395 | TitledOLEDPage(t), | ||
| 396 | ✗ | msg1(m1), | |
| 397 | ✗ | msg2(m2), | |
| 398 | ✗ | items(i), | |
| 399 | ✗ | curSelected(0), | |
| 400 | ✗ | itemSelectedCallback(cb) { | |
| 401 | ✗ | } | |
| 402 | ✗ | void PromptOLEDPage::displaying() { | |
| 403 | ✗ | curSelected = 0; | |
| 404 | ✗ | display(); | |
| 405 | ✗ | } | |
| 406 | ✗ | void PromptOLEDPage::display() { | |
| 407 | ✗ | if (oledForcedOff) | |
| 408 | ✗ | return; | |
| 409 | ✗ | clearDisplay(); | |
| 410 | ✗ | int startY = displayTitle(); | |
| 411 | ✗ | int skipY = numRows <= 3 ? 8 : (numRows == 4 ? 9 : 10); | |
| 412 | |||
| 413 | ✗ | if (numRows > 4) { | |
| 414 | ✗ | startY += skipY; | |
| 415 | } | ||
| 416 | ✗ | if (numRows != 1) { | |
| 417 | ✗ | printStringCentered(startY, msg1); | |
| 418 | ✗ | startY += skipY; | |
| 419 | ✗ | if (numRows > 2) { | |
| 420 | ✗ | printStringCentered(startY, msg2); | |
| 421 | ✗ | startY += skipY; | |
| 422 | } | ||
| 423 | } | ||
| 424 | |||
| 425 | ✗ | if (!items.empty()) { | |
| 426 | ✗ | int skipX = 122 / items.size(); | |
| 427 | ✗ | for (int x = 0; x < items.size(); x++) { | |
| 428 | ✗ | if (x == curSelected) { | |
| 429 | ✗ | fillRect(x * skipX + 4, startY, skipX, 8); | |
| 430 | } | ||
| 431 | ✗ | int l = items[x].length() * 6 / 2; | |
| 432 | ✗ | printString(x * skipX + 4 + skipX / 2 - l, startY, items[x], x != curSelected); | |
| 433 | } | ||
| 434 | } | ||
| 435 | ✗ | flushDisplay(); | |
| 436 | } | ||
| 437 | ✗ | bool PromptOLEDPage::doAction(const std::string& action) { | |
| 438 | ✗ | if (action == "Down" || action == "Test/Down") { | |
| 439 | ✗ | curSelected++; | |
| 440 | ✗ | if (curSelected == items.size()) { | |
| 441 | ✗ | curSelected = 0; | |
| 442 | } | ||
| 443 | ✗ | display(); | |
| 444 | ✗ | } else if (action == "Up") { | |
| 445 | ✗ | if (curSelected) { | |
| 446 | ✗ | curSelected--; | |
| 447 | } else { | ||
| 448 | ✗ | curSelected = items.size() - 1; | |
| 449 | } | ||
| 450 | ✗ | display(); | |
| 451 | ✗ | } else if (action == "Enter") { | |
| 452 | ✗ | ItemSelected(items[curSelected]); | |
| 453 | } | ||
| 454 | ✗ | return false; | |
| 455 | } | ||
| 456 | ✗ | void PromptOLEDPage::ItemSelected(const std::string& item) { | |
| 457 | ✗ | if (itemSelectedCallback) { | |
| 458 | ✗ | itemSelectedCallback(item); | |
| 459 | } | ||
| 460 | ✗ | } | |
| 461 | |||
| 462 | 12 | ListOLEDPage::ListOLEDPage(const std::string& t, const std::vector<std::string>& i, OLEDPage* p) : | |
| 463 | TitledOLEDPage(t), | ||
| 464 | 12 | items(i), | |
| 465 | 12 | curTop(0), | |
| 466 |
1/2✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
|
12 | parent(p) { |
| 467 | 12 | } | |
| 468 | ✗ | void ListOLEDPage::displaying() { | |
| 469 | ✗ | curTop = 0; | |
| 470 | ✗ | display(); | |
| 471 | ✗ | } | |
| 472 | 7 | void ListOLEDPage::displayScrollArrows(int startY) { | |
| 473 |
2/6✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 7 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
|
7 | int maxY = numRows == 11 ? 127 : (numRows == 5 ? 63 : (numRows == 4 ? 47 : 31)); |
| 474 |
1/2✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
|
7 | if (items.size() > numRows) { |
| 475 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
|
7 | if (curTop != 0) { |
| 476 | ✗ | fillTriangle(116, startY + 8, 125, startY + 8, 120, startY); | |
| 477 | } | ||
| 478 |
1/2✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
|
7 | if ((curTop + numRows) < items.size()) { |
| 479 | 7 | fillTriangle(116, maxY - 10, 125, maxY - 10, 120, maxY - 2); | |
| 480 | } | ||
| 481 | } | ||
| 482 | 7 | } | |
| 483 | |||
| 484 | ✗ | void ListOLEDPage::display() { | |
| 485 | ✗ | if (oledForcedOff) | |
| 486 | ✗ | return; | |
| 487 | ✗ | clearDisplay(); | |
| 488 | ✗ | int startY = displayTitle(); | |
| 489 | ✗ | displayScrollArrows(startY); | |
| 490 | ✗ | int skipY = numRows <= 3 ? 8 : (numRows == 4 ? 9 : 10); | |
| 491 | ✗ | int posX = 6; | |
| 492 | ✗ | if (oledType == OLEDPage::OLEDType::TEXT_ONLY) { | |
| 493 | ✗ | posX = 0; | |
| 494 | } | ||
| 495 | ✗ | for (int x = curTop; (x < items.size()) && (x < (curTop + numRows)); ++x) { | |
| 496 | ✗ | printString(posX, startY, items[x]); | |
| 497 | ✗ | startY += skipY; | |
| 498 | } | ||
| 499 | ✗ | flushDisplay(); | |
| 500 | } | ||
| 501 | |||
| 502 | ✗ | bool ListOLEDPage::doAction(const std::string& action) { | |
| 503 | ✗ | if (action == "Down") { | |
| 504 | ✗ | if ((items.size() - curTop) < numRows) { | |
| 505 | ✗ | curTop++; | |
| 506 | } | ||
| 507 | ✗ | display(); | |
| 508 | ✗ | } else if (action == "Test/Down") { | |
| 509 | ✗ | curTop++; | |
| 510 | ✗ | if ((items.size() - curTop) < numRows) { | |
| 511 | ✗ | curTop = 0; | |
| 512 | } | ||
| 513 | ✗ | display(); | |
| 514 | ✗ | } else if (action == "Up") { | |
| 515 | ✗ | if (curTop) { | |
| 516 | ✗ | curTop--; | |
| 517 | } | ||
| 518 | ✗ | display(); | |
| 519 | ✗ | } else if (action == "Enter" || (action == "Back" && parent)) { | |
| 520 | ✗ | SetCurrentPage(parent); | |
| 521 | } | ||
| 522 | ✗ | return true; | |
| 523 | } | ||
| 524 | |||
| 525 | 12 | MenuOLEDPage::MenuOLEDPage(const std::string& t, const std::vector<std::string>& i, OLEDPage* p) : | |
| 526 | ListOLEDPage(t, i, p), | ||
| 527 | 12 | curSelected(0), | |
| 528 | 12 | itemSelectedCallback(nullptr) { | |
| 529 | 12 | } | |
| 530 | ✗ | MenuOLEDPage::MenuOLEDPage(const std::string& t, const std::vector<std::string>& i, | |
| 531 | const std::function<void(const std::string&)>& cb, | ||
| 532 | ✗ | OLEDPage* p) : | |
| 533 | ListOLEDPage(t, i, p), | ||
| 534 | ✗ | curSelected(0), | |
| 535 | ✗ | itemSelectedCallback(cb) { | |
| 536 | ✗ | } | |
| 537 | 5 | void MenuOLEDPage::displaying() { | |
| 538 | 5 | curSelected = 0; | |
| 539 | 5 | curTop = 0; | |
| 540 | 5 | display(); | |
| 541 | 5 | } | |
| 542 | 7 | void MenuOLEDPage::display() { | |
| 543 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
|
7 | if (oledForcedOff) |
| 544 | ✗ | return; | |
| 545 | 7 | clearDisplay(); | |
| 546 | 7 | int startY = displayTitle(); | |
| 547 | 7 | displayScrollArrows(startY); | |
| 548 |
2/4✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 7 times.
|
7 | int skipY = numRows <= 3 ? 8 : (numRows == 4 ? 9 : 10); |
| 549 |
1/2✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
|
7 | int width = items.size() > numRows ? 110 : 120; |
| 550 | 7 | int posX = 6; | |
| 551 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
|
7 | if (oledType == OLEDPage::OLEDType::TEXT_ONLY) { |
| 552 | ✗ | posX = 0; | |
| 553 | } | ||
| 554 |
5/6✓ Branch 0 taken 42 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 35 times.
✓ Branch 3 taken 7 times.
✓ Branch 4 taken 35 times.
✓ Branch 5 taken 7 times.
|
42 | for (int x = curTop; (x < items.size()) && (x < (curTop + numRows)); ++x) { |
| 555 |
2/2✓ Branch 0 taken 7 times.
✓ Branch 1 taken 28 times.
|
35 | if (curSelected == x) { |
| 556 | 7 | fillRect(4, startY, width, 8); | |
| 557 | } | ||
| 558 | 35 | printString(posX, startY, items[x], curSelected != x); | |
| 559 | 35 | startY += skipY; | |
| 560 | } | ||
| 561 | 7 | flushDisplay(); | |
| 562 | } | ||
| 563 | 6 | bool MenuOLEDPage::doAction(const std::string& action) { | |
| 564 | // printf("In menu action %s\n", action.c_str()); | ||
| 565 |
5/6✓ Branch 0 taken 5 times.
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 5 times.
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 5 times.
|
6 | if (action == "Down" || action == "Test/Down") { |
| 566 | 1 | curSelected++; | |
| 567 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (curSelected >= items.size()) { |
| 568 | ✗ | curSelected = 0; | |
| 569 | ✗ | curTop = 0; | |
| 570 | } | ||
| 571 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | while ((curSelected - curTop) >= numRows) { |
| 572 | ✗ | curTop++; | |
| 573 | } | ||
| 574 | 1 | display(); | |
| 575 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 4 times.
|
5 | } else if (action == "Up") { |
| 576 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | if (curSelected) { |
| 577 | 1 | curSelected--; | |
| 578 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (curSelected < curTop) { |
| 579 | ✗ | curTop = curSelected; | |
| 580 | } | ||
| 581 | } else { | ||
| 582 | ✗ | curSelected = items.size() - 1; | |
| 583 | ✗ | curTop = curSelected - numRows + 1; | |
| 584 | ✗ | if (curTop < 0) { | |
| 585 | ✗ | curTop = 0; | |
| 586 | } | ||
| 587 | } | ||
| 588 | 1 | display(); | |
| 589 |
1/2✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
|
4 | } else if (action == "Enter") { |
| 590 | 4 | itemSelected(items[curSelected]); | |
| 591 | ✗ | } else if (action == "Back" && parent) { | |
| 592 | ✗ | SetCurrentPage(parent); | |
| 593 | } | ||
| 594 | 6 | return true; | |
| 595 | } | ||
| 596 | |||
| 597 | ✗ | void MenuOLEDPage::itemSelected(const std::string& item) { | |
| 598 | ✗ | if (itemSelectedCallback) { | |
| 599 | ✗ | itemSelectedCallback(item); | |
| 600 | } | ||
| 601 | ✗ | } | |
| 602 |