Пример #1
0
/*! \fn     getYesNoAnswerInput(uint8_t blocking)
*   \brief  Use the input interface to get user input
*   \param  blocking    Boolean to know if we should wait for input or timeout
*   \note   In case of a non blocking call, caller must call activityDetectedRoutine() & miniWheelClearDetections()
*   \return see mini_input_yes_no_ret_t
*/
RET_TYPE getYesNoAnswerInput(uint8_t blocking)
{
    #if defined(ALWAYS_ACCEPT_REQUESTS)
        return MINI_INPUT_RET_YES;
    #endif

    uint8_t incomingData[RAWHID_TX_SIZE];
    RET_TYPE detect_result;
    
    if (blocking == TRUE)
    {
        // Switch on lights
        activityDetectedRoutine();
        
        // Clear possible remaining detection
        miniWheelClearDetections();
    }
    
    // Wait for a touch press
    do
    {
        // User interaction timeout or smartcard removed
        if ((hasTimerExpired(TIMER_USERINT, TRUE) == TIMER_EXPIRED) || (isSmartCardAbsent() == RETURN_OK))
        {
            return MINI_INPUT_RET_TIMEOUT;
        }
        
        // Read usb comms as the plugin could ask to cancel the request
        if ((getMooltipassParameterInEeprom(USER_REQ_CANCEL_PARAM) != FALSE) && (usbRawHidRecv(incomingData) == RETURN_COM_TRANSF_OK))
        {
            if (incomingData[HID_TYPE_FIELD] == CMD_CANCEL_REQUEST)
            {
                // Request canceled
                return MINI_INPUT_RET_TIMEOUT;
            }
            else
            {
                // Another packet (that shouldn't be sent!), ask to retry later...
                usbSendMessage(CMD_PLEASE_RETRY, 0, incomingData);
            }
        }
        
        // Check if something has been pressed
        detect_result = miniGetWheelAction(FALSE, TRUE);
        if (detect_result == WHEEL_ACTION_SHORT_CLICK)
        {
            return MINI_INPUT_RET_YES;
        }
    }
    while(blocking != FALSE);
    
    // Return MINI_INPUT_RET_NONE if nothing was pressed and no timeout occurred
    return MINI_INPUT_RET_NONE;
}    
Пример #2
0
/*! \fn     guiAskForConfirmation(const char* string)
*   \brief  Ask for user confirmation for different things
*   \param  nb_args     Number of text lines (must be either 1 2 or 3/4 (depending on the MP version))
*   \param  text_object Pointer to the text object if more than 1 line, pointer to the string if not
*   \return User confirmation or not
*/
RET_TYPE guiAskForConfirmation(uint8_t nb_args, confirmationText_t* text_object)
{
    uint8_t flash_flag_set = FALSE;
    uint8_t flash_flag = FALSE;
    uint8_t flash_sm = 0;

    // LED animation
    #ifdef LEDS_ENABLED_MINI
        miniLedsSetAnimation(ANIM_PULSE_UP_RAMP_DOWN);
    #endif
    
    // Check if we want to flash the screen
    if ((nb_args & 0xF0) != 0)
    {
        flash_flag_set = TRUE;
        nb_args = nb_args & 0x0F;
        // Check that the user didn't disable it
        if (getMooltipassParameterInEeprom(FLASH_SCREEN_PARAM) != FALSE)
        {
            flash_flag = TRUE;
        }
    }

    // Variables for scrolling
    uint8_t string_y_indexes[3];
    uint8_t string_extra_chars[3];
    uint8_t string_offset_cntrs[3] = {0,0,0};
    // Display variables
    uint8_t approve_selected = TRUE;
        
    // Draw asking bitmap
    miniOledClearFrameBuffer();
    miniOledSetMaxTextY(SSD1305_OLED_WIDTH-15);
    miniOledBitmapDrawFlash(SSD1305_OLED_WIDTH-15, 0, BITMAP_APPROVE, 0);
        
    // Display lines. 
    // Note: line are truncated at the miniOled driver level when miniOledTextWritingYIncrement is set to FALSE (default)
    if (nb_args == 1)
    {
        miniOledPutCenteredString(THREE_LINE_TEXT_SECOND_POS, (char*)text_object);
    }
    else if (nb_args == 2)
    {
        string_y_indexes[0] = TWO_LINE_TEXT_FIRST_POS;
        string_y_indexes[1] = TWO_LINE_TEXT_SECOND_POS;
    }
    else
    {
        string_y_indexes[0] = THREE_LINE_TEXT_FIRST_POS;
        string_y_indexes[1] = THREE_LINE_TEXT_SECOND_POS;
        string_y_indexes[2] = THREE_LINE_TEXT_THIRD_POS; 
    }
        
    // For loop to display lines when there is more than one arg
    if (nb_args > 1)
    {
        for (uint8_t i = 0; i < nb_args; i++)
        {
            string_extra_chars[i] = strlen(text_object->lines[i]) - miniOledPutCenteredString(string_y_indexes[i], text_object->lines[i]);
        }
    }
        
    miniOledFlushEntireBufferToDisplay();
    miniOledResetMaxTextY();
    
    // Wait for user input
    RET_TYPE input_answer = MINI_INPUT_RET_NONE;
    RET_TYPE detect_result;
        
    // Switch on lights
    activityDetectedRoutine();
        
    // Clear possible remaining detection
    miniWheelClearDetections();
        
    // Arm timer for scrolling (caps timer that isn't relevant here)
    activateTimer(TIMER_CAPS, SCROLLING_DEL);

    // Arm timer for flashing
    activateTimer(TIMER_FLASHING, 500);
        
    // Loop while no timeout occurs or no button is pressed
    while (input_answer == MINI_INPUT_RET_NONE)
    {
        // User interaction timeout or smartcard removed
        if ((hasTimerExpired(TIMER_USERINT, TRUE) == TIMER_EXPIRED) || (isSmartCardAbsent() == RETURN_OK))
        {
            input_answer = MINI_INPUT_RET_TIMEOUT;
        }
            
        // Read usb comms as the plugin could ask to cancel the request
        if (usbCancelRequestReceived() == RETURN_OK)
        {
            input_answer = MINI_INPUT_RET_TIMEOUT;
        }

        // Screen flashing logic
        if ((hasTimerExpired(TIMER_FLASHING, TRUE) == TIMER_EXPIRED) && (flash_flag == TRUE) && (flash_sm < 4))
        {
            // Look at the flash_sm LSb to know what is the display state
            if ((flash_sm++ & 0x01) != 0x00)
            {
                miniOledNormalDisplay();
            } 
            else
            {
                miniOledInvertedDisplay();
            }
            // Re-arm timer
            activateTimer(TIMER_FLASHING, 500);
        }
            
        // Check if something has been pressed
        detect_result = miniGetWheelAction(FALSE, TRUE);
        if (detect_result == WHEEL_ACTION_SHORT_CLICK)
        {
            input_answer = MINI_INPUT_RET_YES;
        }
        else if (detect_result == WHEEL_ACTION_LONG_CLICK)
        {
            input_answer = MINI_INPUT_RET_BACK;
        }

        // Knock to approve
        #if defined(HARDWARE_MINI_CLICK_V2)
        if ((scanAndGetDoubleZTap(FALSE) == ACC_RET_KNOCK) && (flash_flag_set != FALSE))
        {
            input_answer = MINI_INPUT_RET_YES;
        }
        #else
        (void)flash_flag_set;
        #endif
            
        // Text scrolling
        if ((hasTimerExpired(TIMER_CAPS, FALSE) == TIMER_EXPIRED) && (nb_args > 1))
        {
            miniOledClearFrameBuffer();
            activateTimer(TIMER_CAPS, SCROLLING_DEL);
            miniOledSetMaxTextY(SSD1305_OLED_WIDTH-15);
            if(approve_selected == FALSE)
            {
                miniOledBitmapDrawFlash(SSD1305_OLED_WIDTH-15, 0, BITMAP_DENY, 0);
            }
            else
            {
                miniOledBitmapDrawFlash(SSD1305_OLED_WIDTH-15, 0, BITMAP_APPROVE, 0);
            }

            for (uint8_t i = 0; i < nb_args; i++)
            {
                if (string_extra_chars[i] > 0)
                {
                    miniOledPutCenteredString(string_y_indexes[i], (text_object->lines[i]) + string_offset_cntrs[i]);
                        
                    if (string_offset_cntrs[i]++ == string_extra_chars[i])
                    {
                        string_offset_cntrs[i] = 0;
                    }
                }
                else
                {
                    miniOledPutCenteredString(string_y_indexes[i], text_object->lines[i]);
                }
            }
            miniOledFlushEntireBufferToDisplay();
            miniOledResetMaxTextY();
        }

        // Approve / deny display change
        if (getWheelCurrentIncrement() != 0)
        {
            if(approve_selected == FALSE)
            {
                miniOledBitmapDrawFlash(SSD1305_OLED_WIDTH-15, 0, BITMAP_APPROVE, 0);
            }
            else
            {
                miniOledBitmapDrawFlash(SSD1305_OLED_WIDTH-15, 0, BITMAP_DENY, 0);
            }
            approve_selected = !approve_selected;
            miniOledFlushEntireBufferToDisplay();
        }
    } 
    
    // In case display was inverted, set it normally
    miniOledNormalDisplay();  
    
    if ((input_answer == MINI_INPUT_RET_YES) && (approve_selected != FALSE))
    {
        // LED animation
        #ifdef LEDS_ENABLED_MINI
            miniLedsSetAnimation(ANIM_NONE);
        #endif
        return RETURN_OK;
    }
    else if (input_answer == MINI_INPUT_RET_BACK)
    {
        // LED animation
        #ifdef LEDS_ENABLED_MINI
            miniLedsSetAnimation(ANIM_NONE);
        #endif
        return RETURN_BACK;
    }
    else
    {
        // LED animation
        #ifdef LEDS_ENABLED_MINI
            miniLedsSetAnimation(ANIM_NONE);
        #endif
        return RETURN_NOK;
    }
}
Пример #3
0
/*! \fn     guiScreenLoop(uint8_t input_interface_result)
*   \brief  Function called to handle screen changes
*   \param  input_interface_result Touch detection result
*/
void guiScreenLoop(uint8_t input_interface_result)
{
    // If no press, you can return!
    if ((input_interface_result == WHEEL_ACTION_NONE) || (currentScreen == SCREEN_DEFAULT_INSERTED_INVALID) || (currentScreen == SCREEN_DEFAULT_INSERTED_UNKNOWN))
    {
        return;
    }

    if (currentScreen == SCREEN_DEFAULT_NINSERTED)
    {
        // No smart card inserted, ask the user to insert one
        guiDisplayInsertSmartCardScreenAndWait();
    }
    else if (currentScreen == SCREEN_MEMORY_MGMT)
    {
        // Currently in memory management mode, tell the user to finish it via the plugin/app
        guiDisplayInformationOnScreenAndWait(ID_STRING_CLOSEMEMMGMT);
        guiGetBackToCurrentScreen();
        miniWheelClearDetections();
    }
    else if (currentScreen == SCREEN_DEFAULT_INSERTED_LCK)
    {
        // Locked screen and a detection happened, check that the user hasn't removed his card, launch unlocking process
        if ((cardDetectedRoutine() == RETURN_MOOLTIPASS_USER) && (validCardDetectedFunction(0, TRUE) == RETURN_VCARD_OK))
        {
            // User approved his pin
            unlockFeatureCheck();
            currentScreen = SCREEN_DEFAULT_INSERTED_NLCK;
        }
            
        // Go to the new screen
        guiGetBackToCurrentScreen();
    }
    else
    {
        if (input_interface_result == WHEEL_ACTION_UP)
        {
            // We can do that because of defines and bitmap order (see logic_fw_flash_storage and gui.h)
            if (currentScreen == SCREEN_LOCK)
            {
                currentScreen = SCREEN_SETTINGS;
            } 
            else if (currentScreen == SCREEN_SETTINGS_CHANGE_PIN)
            {
                currentScreen = SCREEN_SETTINGS_ERASE;
            }
            else
            {
                currentScreen--;
            }
            // We can do that because of defines and bitmap order (see logic_fw_flash_storage and gui.h)
            for (uint8_t i = 0; i < NB_BMPS_PER_TRANSITION; i++)
            {
                miniOledBitmapDrawFlash(0, 0, (currentScreen-SCREEN_LOCK)*NB_BMPS_PER_TRANSITION+BITMAP_MAIN_LOCK+NB_BMPS_PER_TRANSITION-1-i, OLED_SCROLL_FLIP);
                timerBasedDelayMs(12);
            }
        }
        else if (input_interface_result == WHEEL_ACTION_DOWN)
        {
            // We can do that because of defines and bitmap order (see logic_fw_flash_storage and gui.h)
            for (uint8_t i = 0; i < NB_BMPS_PER_TRANSITION-1; i++)
            {
                miniOledBitmapDrawFlash(0, 0, (currentScreen-SCREEN_LOCK)*NB_BMPS_PER_TRANSITION+BITMAP_MAIN_LOCK+1+i, OLED_SCROLL_FLIP);
                timerBasedDelayMs(12);
            }
            if (currentScreen == SCREEN_SETTINGS)
            {
                currentScreen = SCREEN_LOCK;
            }
            else if (currentScreen == SCREEN_SETTINGS_ERASE)
            {
                currentScreen = SCREEN_SETTINGS_CHANGE_PIN;
            }
            else
            {
                currentScreen++;
            }
            miniOledBitmapDrawFlash(0, 0, (currentScreen-SCREEN_LOCK)*NB_BMPS_PER_TRANSITION+BITMAP_MAIN_LOCK, OLED_SCROLL_FLIP);              
        }
        else if (input_interface_result == WHEEL_ACTION_LONG_CLICK)
        {
            // Long press in main menu : lock, long press in settings menu: go back to login screen
            if ((currentScreen >= SCREEN_SETTINGS_CHANGE_PIN) && (currentScreen <= SCREEN_SETTINGS_ERASE))
            {
                currentScreen = SCREEN_LOGIN;
                miniOledBitmapDrawFlash(0, 0, (currentScreen-SCREEN_LOCK)*NB_BMPS_PER_TRANSITION+BITMAP_MAIN_LOCK, OLED_SCROLL_UP);
            }
        }
        else if (input_interface_result == WHEEL_ACTION_SHORT_CLICK)
        {
            switch(currentScreen)
            {
                case SCREEN_LOCK:
                {
                    // User wants to lock his mooltipass
                    currentScreen = SCREEN_DEFAULT_INSERTED_LCK;
                    handleSmartcardRemoved();
                    guiGetBackToCurrentScreen();
                    break;
                }
                case SCREEN_LOGIN:
                {
                    // User wants to go to the login menu
                    if (getStartingParentAddress() != NODE_ADDR_NULL)
                    {
                        loginSelectLogic();
                    }
                    else
                    {
                        guiDisplayInformationOnScreenAndWait(ID_STRING_NO_CREDS);
                    }
                    guiGetBackToCurrentScreen();
                    break;
                }
                case SCREEN_FAVORITES:
                {
                    // User wants to go to the favorite menu
                    #if defined(ENABLE_CREDENTIAL_MANAGEMENT) && defined(REPLACE_FAVORITES_WITH_CREDENTIAL_MANAGEMENT)
                        managementActionPickingLogic();
                    #else
                        favoritePickingLogic();
                    #endif
                    guiGetBackToCurrentScreen();
                    break;
                }
                case SCREEN_SETTINGS:
                {
                    currentScreen = SCREEN_SETTINGS_CHANGE_PIN;
                    miniOledBitmapDrawFlash(0, 0, (currentScreen-SCREEN_LOCK)*NB_BMPS_PER_TRANSITION+BITMAP_MAIN_LOCK, OLED_SCROLL_UP);
                    break;
                }
                case SCREEN_SETTINGS_HOME:
                {
                    currentScreen = SCREEN_LOGIN;
                    miniOledBitmapDrawFlash(0, 0, (currentScreen-SCREEN_LOCK)*NB_BMPS_PER_TRANSITION+BITMAP_MAIN_LOCK, OLED_SCROLL_UP);
                    break;
                }
                case SCREEN_SETTINGS_CHANGE_PIN:
                {
                    // User wants to change his PIN code
                        
                    // Reauth user
                    if (removeCardAndReAuthUser() == RETURN_OK)
                    {
                        // User approved his pin, ask his new one
                        volatile uint16_t pin_code;
                            
                        if (guiAskForNewPin(&pin_code, ID_STRING_NEW_PINQ) == RETURN_NEW_PIN_OK)
                        {
                            // User successfully entered a new pin
                            writeSecurityCode(&pin_code);
                            // Inform of success
                            guiDisplayInformationOnScreenAndWait(ID_STRING_PIN_CHANGED);
                        }
                        else
                        {
                            // Inform of fail
                            guiDisplayInformationOnScreenAndWait(ID_STRING_PIN_NCGHANGED);
                        }
                        pin_code = 0x0000;
                    }
                    else
                    {
                        currentScreen = SCREEN_DEFAULT_INSERTED_LCK;
                    }
                    guiGetBackToCurrentScreen();
                    break;
                }
                case SCREEN_SETTINGS_BACKUP:
                {
                    // User wants to clone his smartcard
                    volatile uint16_t pin_code;
                    RET_TYPE temp_rettype;
                        
                    // Reauth user
                    if (removeCardAndReAuthUser() == RETURN_OK)
                    {
                        // Ask for new pin
                        temp_rettype = guiAskForNewPin(&pin_code, ID_STRING_PIN_NEW_CARD);
                        if (temp_rettype == RETURN_NEW_PIN_OK)
                        {
                            // Start the cloning process
                            if (cloneSmartCardProcess(&pin_code) == RETURN_OK)
                            {
                                // Well it worked....
                            }
                            else
                            {
                                currentScreen = SCREEN_DEFAULT_INSERTED_LCK;
                                guiDisplayInformationOnScreen(ID_STRING_TGT_CARD_NBL);
                            }
                            pin_code = 0x0000;
                        }
                        else if (temp_rettype == RETURN_NEW_PIN_DIFF)
                        {
                            currentScreen = SCREEN_DEFAULT_INSERTED_LCK;
                            guiDisplayInformationOnScreen(ID_STRING_PIN_DIFF);
                        }
                        else
                        {
                            guiGetBackToCurrentScreen();
                            return;
                        }
                    }
                    else
                    {
                        currentScreen = SCREEN_DEFAULT_INSERTED_LCK;
                        guiDisplayInformationOnScreen(ID_STRING_FAILED);
                    }
                    userViewDelay();
                    guiGetBackToCurrentScreen();
                    break;
                }
                case SCREEN_SETTINGS_ERASE:
                {
                    // User wants to delete his profile in flash / eeprom....
                    if ((guiAskForConfirmation(1, (confirmationText_t*)readStoredStringToBuffer(ID_STRING_AREYOUSURE)) == RETURN_OK) && (removeCardAndReAuthUser() == RETURN_OK) && (guiAskForConfirmation(1, (confirmationText_t*)readStoredStringToBuffer(ID_STRING_AREYOURLSURE)) == RETURN_OK))
                    {
                        uint8_t currentuserid = getCurrentUserID();
                        guiDisplayProcessingScreen();
                        deleteCurrentUserFromFlash();
                            
                        if (guiAskForConfirmation(1, (confirmationText_t*)readStoredStringToBuffer(ID_STRING_ERASE_TCARD)) == RETURN_OK)
                        {
                            guiDisplayProcessingScreen();
                            eraseSmartCard();
                                
                            // Erase other smartcards
                            while (guiAskForConfirmation(1, (confirmationText_t*)readStoredStringToBuffer(ID_STRING_OTHECARDFUSER)) == RETURN_OK)
                            {
                                // Ask the user to insert other smartcards
                                guiDisplayInformationOnScreen(ID_STRING_INSERT_OTHER);
                                    
                                // Wait for the user to remove and enter another smartcard
                                while (isCardPlugged() != RETURN_JRELEASED);
                                    
                                // Wait for the user to insert a new smart card
                                while (isCardPlugged() != RETURN_JDETECT);
                                guiDisplayProcessingScreen();
                                    
                                // Check the card type & ask user to enter his pin, check that the new user id loaded by validCardDetectedFunction is still the same
                                if ((cardDetectedRoutine() == RETURN_MOOLTIPASS_USER) && (validCardDetectedFunction(0, FALSE) == RETURN_VCARD_OK) && (currentuserid == getCurrentUserID()))
                                {
                                    eraseSmartCard();
                                }
                            }
                        }
                            
                        // Delete LUT entries
                        guiDisplayProcessingScreen();
                        deleteUserIdFromSMCUIDLUT(currentuserid);
                            
                        // Go to invalid screen
                        currentScreen = SCREEN_DEFAULT_INSERTED_INVALID;
                    }
                    else
                    {
                        currentScreen = SCREEN_DEFAULT_INSERTED_LCK;
                    }
                    userViewDelay();
                    handleSmartcardRemoved();
                    guiGetBackToCurrentScreen();
                    break;
                }

                default: break;
            }                
        }
    }
}
Пример #4
0
/*! \fn     guiAskForConfirmation(const char* string)
*   \brief  Ask for user confirmation for different things
*   \param  nb_args     Number of text lines (must be either 1 2 or 3/4 (depending on the MP version))
*   \param  text_object Pointer to the text object if more than 1 line, pointer to the string if not
*   \return User confirmation or not
*/
RET_TYPE guiAskForConfirmation(uint8_t nb_args, confirmationText_t* text_object)
{    
    uint8_t flash_flag = FALSE;
    
    // Check if we want to flash the screen
    if ((nb_args & 0xF0) != 0)
    {
        nb_args = nb_args & 0x0F;
        // Check that the user didn't disable it
        if (getMooltipassParameterInEeprom(FLASH_SCREEN_PARAM) != FALSE)
        {
            flash_flag = TRUE;
        }
    }
    
    #if defined(HARDWARE_OLIVIER_V1)
        // Temp string for truncating
        char string_tbd[31];
        string_tbd[30] = 0;
        
        // Draw asking bitmap
        oledClear();
        oledBitmapDrawFlash(0, 0, BITMAP_YES_NO_INT_L, 0);
        oledBitmapDrawFlash(222, 0, BITMAP_YES_NO_INT_R, 0);
    
        // If more than one line
        if (nb_args == 1)
        {
            // Yeah, that's a bit dirty
            oledPutstrXY(0, 24, OLED_CENTRE, (char*)text_object);
        }
        else
        {
            while (nb_args--)
            {
                // Truncate and then display string
                memcpy(string_tbd, text_object->lines[nb_args], 30);
                oledPutstrXY(0, 2 + (nb_args << 4), OLED_CENTRE, string_tbd);
            }
        }
    
        // Display result
        oledDisplayOtherBuffer();
    #elif defined(MINI_VERSION)
        // Variables for scrolling
        uint8_t string_y_indexes[3];
        uint8_t string_extra_chars[3];
        uint8_t string_offset_cntrs[3] = {0,0,0};
        // Display variables
        uint8_t approve_selected = TRUE;
        
        // Draw asking bitmap
        oledClear();
        miniOledSetMaxTextY(SSD1305_OLED_WIDTH-15);
        oledBitmapDrawFlash(SSD1305_OLED_WIDTH-15, 0, BITMAP_APPROVE, 0);
        
        // Display lines. 
        // Note: line are truncated at the oled driver level when miniOledTextWritingYIncrement is set to FALSE
        if (nb_args == 1)
        {
            miniOledPutCenteredString(THREE_LINE_TEXT_SECOND_POS, (char*)text_object);
        }
        else if (nb_args == 2)
        {
            string_y_indexes[0] = TWO_LINE_TEXT_FIRST_POS;
            string_y_indexes[1] = TWO_LINE_TEXT_SECOND_POS;
        }
        else
        {
            string_y_indexes[0] = THREE_LINE_TEXT_FIRST_POS;
            string_y_indexes[1] = THREE_LINE_TEXT_SECOND_POS;
            string_y_indexes[2] = THREE_LINE_TEXT_THIRD_POS; 
        }
        
        // For loop to display lines when there is more than one arg
        if (nb_args > 1)
        {
            for (uint8_t i = 0; i < nb_args; i++)
            {
                string_extra_chars[i] = strlen(text_object->lines[i]) - miniOledPutCenteredString(string_y_indexes[i], text_object->lines[i]);
            }
        }
        
        miniOledFlushEntireBufferToDisplay();
        miniOledResetMaxTextY();
    #endif

    // In case the display inverted, set it correctly
    if (flash_flag == TRUE)
    {
        activityDetectedRoutine();
        oledInvertedDisplay();
        timerBased500MsDelay();
        oledNormalDisplay();
        timerBased500MsDelay();
        oledInvertedDisplay();
        timerBased500MsDelay();
        oledNormalDisplay();
    }
    
    // Wait for user input
    #if defined(HARDWARE_OLIVIER_V1)
        if(getTouchedPositionAnswer(LED_MASK_WHEEL) == TOUCHPOS_RIGHT)
        {
            return RETURN_OK;
        }
        else
        {
            return RETURN_NOK;
        }
    #elif defined(MINI_VERSION)
        RET_TYPE input_answer = MINI_INPUT_RET_NONE;
        
        // Switch on lights
        activityDetectedRoutine();
        
        // Clear possible remaining detection
        miniWheelClearDetections();
        
        // Arm timer for scrolling (caps timer that isn't relevant here)
        activateTimer(TIMER_CAPS, SCROLLING_DEL);
        
        // Loop while no timeout occurs or no button is pressed
        while (input_answer == MINI_INPUT_RET_NONE)
        {
            input_answer = getYesNoAnswerInput(FALSE);
            
            // Text scrolling
            if ((hasTimerExpired(TIMER_CAPS, TRUE) == TIMER_EXPIRED) && (nb_args > 1))
            {
                miniOledDrawRectangle(0, 0, SSD1305_OLED_WIDTH-15, SSD1305_OLED_HEIGHT, FALSE);
                activateTimer(TIMER_CAPS, SCROLLING_DEL);
                miniOledSetMaxTextY(SSD1305_OLED_WIDTH-15);
                for (uint8_t i = 0; i < nb_args; i++)
                {
                    if (string_extra_chars[i] > 0)
                    {
                        miniOledPutCenteredString(string_y_indexes[i], (text_object->lines[i]) + string_offset_cntrs[i]);
                        
                        if (string_offset_cntrs[i]++ == string_extra_chars[i])
                        {
                            string_offset_cntrs[i] = 0;
                        }
                    }
                    else
                    {
                        miniOledPutCenteredString(string_y_indexes[i], text_object->lines[i]);
                    }
                }
                miniOledFlushEntireBufferToDisplay();
                miniOledResetMaxTextY();
            }

            // Approve / deny display change
            if (getWheelCurrentIncrement() != 0)
            {
                if(approve_selected == FALSE)
                {
                    oledBitmapDrawFlash(SSD1305_OLED_WIDTH-15, 0, BITMAP_APPROVE, 0);
                }
                else
                {
                    oledBitmapDrawFlash(SSD1305_OLED_WIDTH-15, 0, BITMAP_DENY, 0);
                }
                approve_selected = !approve_selected;
                miniOledFlushEntireBufferToDisplay();
            }
        }   
    
        if ((input_answer == MINI_INPUT_RET_YES) && (approve_selected != FALSE))
        {
            return RETURN_OK;
        }
        else
        {
            return RETURN_NOK;
        }
    #endif
}