taler-xotp_fw

xOTP generator firmware
Log | Files | Refs | Submodules | README

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 }