frontend.c (9348B)
1 /** 2 * @file frontend.c 3 * @author Adrian STEINER (steia19@bfh.ch) 4 * @brief Frontend source code to set the desired display output 5 * @version 0.1 6 * @date 27-02-2025 7 * 8 * @copyright (C) 2025 Adrian STEINER 9 * This program is free software: you can redistribute it and/or modify 10 * it under the terms of the GNU General Public License as published by 11 * the Free Software Foundation, either version 3 of the License, or 12 * (at your option) any later version. 13 * 14 * This program is distributed in the hope that it will be useful, 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 * GNU General Public License for more details. 18 * 19 * You should have received a copy of the GNU General Public License 20 * along with this program. If not, see <https: //www.gnu.org/licenses/>. 21 * 22 */ 23 #include "frontend.h" 24 25 #include "pbm_graphics.h" 26 #include "pbm_types.h" 27 28 #include <stdarg.h> 29 #include <stdio.h> 30 #include <string.h> 31 32 #include "logger.h" 33 #include "xtotpConfig.h" 34 #include "xtotpUtil.h" 35 36 #define BORDER_TOP (1) 37 #define BORDER_LEFT (1) 38 #define BORDER_RIGHT (1) 39 #define LINE_BORDER (1) 40 #define LINE_SIZE (1) 41 42 char bufferSmall[64]; 43 char bufferBig[64]; 44 45 void frontend_clear(xtotp_Data *appData) 46 { 47 pbm_fill(appData->iDisplay.image, !appData->iDisplay.theme); 48 } 49 50 void frontend_writeStatusIcons(const xtotp_Data *appData) 51 { 52 #define BATTERY_SYMBOL_DELTA (4U) ///< 5 Levels of battery symbols are used 53 #define BATTERY_FULL (100U) ///< 100% as maximum state 54 uint8_t chargeLevel = batMeas_getBatteryPercentState(&appData->iBatMeas); 55 char chargeSymbol = 0; 56 switch (appData->iBatMeas.chargingStatus) { 57 case BAT_FULL: 58 chargeSymbol = appData->iDisplay.symbolBatteryStart; 59 break; 60 case BAT_DISCHARGE: 61 case BAT_CHARGING: 62 chargeSymbol = 63 (chargeLevel == 0) 64 ? appData->iDisplay.symbolBatteryStart + BATTERY_SYMBOL_DELTA 65 : appData->iDisplay.symbolBatteryStart + 66 ((BATTERY_SYMBOL_DELTA - 1) - 67 ((chargeLevel - 1) / (BATTERY_FULL / BATTERY_SYMBOL_DELTA))); 68 break; 69 case BAT_UNKNOWN: 70 chargeSymbol = '?'; 71 break; 72 73 default: 74 break; 75 } 76 77 snprintf(bufferSmall, sizeof(bufferSmall), "%c%3u%%%c%c", chargeSymbol, 78 chargeLevel, 79 ((BAT_CHARGING == appData->iBatMeas.chargingStatus || 80 BAT_FULL == appData->iBatMeas.chargingStatus) 81 ? appData->iDisplay.symbolCharge 82 : ' '), 83 ((appData->iSync.isSynchronizing) 84 ? appData->iDisplay.symbolSynchronize 85 : ' ')); 86 87 pbm_writeString(appData->iDisplay.image, BORDER_LEFT, BORDER_TOP, 88 appData->iDisplay.theme, appData->iDisplay.dispFontSmall, 89 PBM_STRING_LEFT_TOP, bufferSmall); 90 } 91 92 void frontend_updateMenuTitle(xtotp_Data *appData, const char *menuTitle) 93 { 94 if (NULL == appData || NULL == menuTitle) { 95 return; 96 } 97 pbm_writeString(appData->iDisplay.image, 98 appData->iDisplay.image->width - BORDER_RIGHT, BORDER_TOP, 99 appData->iDisplay.theme, appData->iDisplay.dispFontSmall, 100 PBM_STRING_RIGHT_TOP, menuTitle); 101 } 102 103 uint32_t frontend_updateDatafield(xtotp_Data *appData, 104 frontend_DataFieldPos position, 105 const char *format, 106 ...) 107 { 108 /* Create string */ 109 va_list args; 110 va_start(args, format); 111 uint32_t baseYPos = BORDER_TOP + appData->iDisplay.dispFontSmall->height + 112 2 * LINE_BORDER + LINE_SIZE; 113 frontend_clearDatafield(appData, position); 114 switch (position) { 115 case FRONTEND_DATAFIELD_CENTRED: 116 vsnprintf(bufferBig, sizeof(bufferBig), format, args); 117 va_end(args); 118 pbm_writeString(appData->iDisplay.image, 119 appData->iDisplay.image->width - BORDER_RIGHT, baseYPos, 120 appData->iDisplay.theme, appData->iDisplay.dispFontBig, 121 PBM_STRING_RIGHT_TOP, bufferBig); 122 break; 123 case FRONTEND_DATAFIELD_LINE1: 124 vsnprintf(bufferSmall, sizeof(bufferSmall), format, args); 125 va_end(args); 126 pbm_writeString(appData->iDisplay.image, 127 appData->iDisplay.image->width - BORDER_RIGHT, baseYPos, 128 appData->iDisplay.theme, appData->iDisplay.dispFontSmall, 129 PBM_STRING_RIGHT_TOP, bufferSmall); 130 break; 131 case FRONTEND_DATAFIELD_LINE2: 132 vsnprintf(bufferSmall, sizeof(bufferSmall), format, args); 133 va_end(args); 134 baseYPos += appData->iDisplay.dispFontSmall->height + LINE_SIZE; 135 pbm_writeString(appData->iDisplay.image, 136 appData->iDisplay.image->width - BORDER_RIGHT, baseYPos, 137 appData->iDisplay.theme, appData->iDisplay.dispFontSmall, 138 PBM_STRING_RIGHT_TOP, bufferSmall); 139 break; 140 default: 141 va_end(args); 142 return 0; 143 } 144 return baseYPos; 145 } 146 147 void frontend_createBaseWindow(xtotp_Data *appData, 148 const char *menuTitle, 149 const char *data) 150 { 151 frontend_clear(appData); 152 frontend_writeStatusIcons(appData); 153 frontend_updateMenuTitle(appData, menuTitle); 154 uint32_t linePos = 155 BORDER_TOP + appData->iDisplay.dispFontSmall->height + LINE_BORDER; 156 pbm_drawLine(appData->iDisplay.image, 0, linePos, 157 appData->iDisplay.image->width, linePos, 158 appData->iDisplay.theme); 159 if (data) { 160 frontend_updateDatafield(appData, FRONTEND_DATAFIELD_CENTRED, data); 161 } 162 } 163 164 void frontend_showSecretState(xtotp_Data *appData) 165 { 166 #define MAX_DISPLAY_NUMBERS (11) 167 #define INPUT_AS_STRING_SIZE (4) 168 169 uint32_t baseYPos = BORDER_TOP + appData->iDisplay.dispFontSmall->height + 170 2 * LINE_BORDER + LINE_SIZE; 171 pbm_font *usedFont = appData->iDisplay.dispFontSmall; 172 pbm_colors usedColor = appData->iDisplay.theme; 173 uint32_t xPos = appData->iDisplay.image->width - BORDER_RIGHT; 174 uint32_t decimalNumber = (appData->cryptoHandler.processedAlgorithm * 10); 175 uint32_t unitPlace = appData->cryptoHandler.processedAlgorithm % 10; 176 frontend_clearDatafield(appData, FRONTEND_DATAFIELD_CENTRED); 177 // Show current input with checking the current input 178 frontend_updateDatafield(appData, FRONTEND_DATAFIELD_LINE1, 179 "Input secret: "); 180 char inputAsString[INPUT_AS_STRING_SIZE]; 181 snprintf(inputAsString, INPUT_AS_STRING_SIZE, "%u", 182 appData->cryptoHandler.processedAlgorithm); 183 if (crypto_getAlgoInfo( 184 &appData->cryptoHandler 185 .algorithms[appData->cryptoHandler.processedAlgorithm]) != 186 ALGO_UNINITIALIZED) { 187 pbm_writeString(appData->iDisplay.image, xPos, baseYPos, !usedColor, 188 usedFont, PBM_STRING_RIGHT_TOP, inputAsString); 189 } else { 190 pbm_writeString(appData->iDisplay.image, xPos, baseYPos, usedColor, 191 usedFont, PBM_STRING_RIGHT_TOP, inputAsString); 192 } 193 baseYPos += appData->iDisplay.dispFontSmall->height + LINE_SIZE; 194 xPos -= usedFont->width; 195 if (XTOTP_STORABLE_SECRETS >= 10 && decimalNumber < XTOTP_STORABLE_SECRETS) { 196 // Display second line for decimal secrets 197 for (int8_t i = XTOTP_MIN(XTOTP_STORABLE_SECRETS - decimalNumber, 9); 198 i >= 0; i--) { 199 if (crypto_getAlgoInfo( 200 &appData->cryptoHandler.algorithms[i + decimalNumber]) != 201 ALGO_UNINITIALIZED) { 202 usedColor = !appData->iDisplay.theme; 203 } else { 204 usedColor = appData->iDisplay.theme; 205 } 206 pbm_writeChar(appData->iDisplay.image, xPos, baseYPos, usedColor, 207 usedFont, i + '0'); 208 xPos -= (usedFont->width); 209 pbm_writeChar(appData->iDisplay.image, xPos, baseYPos, usedColor, 210 usedFont, unitPlace + '0'); 211 xPos -= (usedFont->width + 1); 212 } 213 } else { 214 frontend_updateDatafield(appData, FRONTEND_DATAFIELD_LINE2, 215 "MAX: %3u ", XTOTP_STORABLE_SECRETS); 216 for (uint8_t i = XTOTP_MIN(XTOTP_STORABLE_SECRETS, 9); i > 0; i--) { 217 pbm_colors usedColor = appData->iDisplay.theme; 218 if (crypto_getAlgoInfo(&appData->cryptoHandler.algorithms[i]) != 219 ALGO_UNINITIALIZED) { 220 usedColor = !appData->iDisplay.theme; 221 } else { 222 usedColor = appData->iDisplay.theme; 223 } 224 pbm_writeChar(appData->iDisplay.image, xPos, baseYPos, usedColor, 225 usedFont, i + '0'); 226 xPos -= (usedFont->width + 1); 227 } 228 } 229 } 230 231 void frontend_clearDatafield(xtotp_Data *appData, 232 frontend_DataFieldPos position) 233 { 234 uint32_t startYPos = BORDER_TOP + appData->iDisplay.dispFontSmall->height + 235 2 * LINE_BORDER + LINE_SIZE; 236 uint32_t endYPos = appData->iDisplay.image->height; 237 switch (position) { 238 case FRONTEND_DATAFIELD_CENTRED: 239 // Clear all, nothing to do 240 break; 241 case FRONTEND_DATAFIELD_LINE1: 242 // end position until line 2 243 endYPos = appData->iDisplay.dispFontSmall->height; 244 break; 245 case FRONTEND_DATAFIELD_LINE2: 246 startYPos += appData->iDisplay.dispFontSmall->height; 247 break; 248 default: 249 return; 250 break; 251 } 252 for (uint32_t y = startYPos; y < endYPos; y++) { 253 pbm_drawLine(appData->iDisplay.image, 0, y, PBM_IMAGE_END, y, 254 !appData->iDisplay.theme); 255 } 256 }