/** * @file tui.cpp * @brief display user interface * @author hendrik schutter * @date 03.08.2020 */ #include "../include/reHDD.h" static std::mutex mxUIrefresh; TUI::TUI(void) { } /** * \brief wipe drive with shred * \param pointer of Drive instance * \return void */ void TUI::initTUI() { initscr(); raw(); keypad(stdscr, TRUE); if (has_colors() == TRUE) { start_color(); } else { printf("Your terminal does not support color\n"); Logger::logThis()->error("Your terminal does not support color"); exit(1); } clear(); curs_set(0); noecho(); cbreak(); init_pair(COLOR_AREA_STDSCR, COLOR_WHITE, COLOR_BLUE); wbkgd(stdscr, COLOR_PAIR(COLOR_AREA_STDSCR)); init_pair(COLOR_AREA_ENTRY_EVEN, COLOR_BLACK, COLOR_WHITE); init_pair(COLOR_AREA_ENTRY_ODD, COLOR_BLUE, COLOR_WHITE); init_pair(COLOR_AREA_ENTRY_SELECTED, COLOR_WHITE, COLOR_RED); init_pair(COLOR_AREA_OVERVIEW, COLOR_BLACK, COLOR_WHITE); init_pair(COLOR_AREA_DETAIL, COLOR_BLACK, COLOR_WHITE); mvprintw(0, 2, "reHDD - HDD refurbishing tool - GPL 3.0 "); Logger::logThis()->info("UI successfully initialized"); } void TUI::updateTUI(list *plistDrives, uint8_t u8SelectedEntry) { mxUIrefresh.lock(); uint16_t u16StdscrX, u16StdscrY; getmaxyx(stdscr, u16StdscrY, u16StdscrX); init_pair(COLOR_AREA_STDSCR, COLOR_WHITE, COLOR_BLUE); wbkgd(stdscr, COLOR_PAIR(COLOR_AREA_STDSCR)); refresh(); // overview window is 3/7 of the x-size overview = createOverViewWindow((int)(u16StdscrX * (float)(3.0 / 7.0)), (u16StdscrY - 1)); wrefresh(overview); // system stat window is 2/7 of the x-size systemview = createSystemStats(((int)(u16StdscrX * (float)(2.0 / 7.0))) - 6, 12, (int)(u16StdscrX * (float)(5.0 / 7.0) + 4), (u16StdscrY - 13)); wrefresh(systemview); delwin(detailview); list::iterator it; uint8_t u8Index = 0U; for (it = plistDrives->begin(); it != plistDrives->end(); ++it) { string sModelFamily = it->getModelFamily(); string sSerial = "SN: " + it->getSerial(); string sCapacity = it->sCapacityToText(); string sState = " "; string sSpeed = " "; string sTime = " "; string sTemp = it->sTemperatureToText(); bool bSelectedEntry = false; if (u8SelectedEntry == u8Index) { bSelectedEntry = true; // mark this drive in entries list displaySelectedDrive(*it, u16StdscrX, u16StdscrY); if ((it->getPowerOnHours() >= WORSE_HOURS) || (it->getPowerCycles() >= WORSE_POWERUP) || (it->getErrorCount() > 0) || (it->getTemperature() >= WORSE_TEMPERATURE)) { // smart values are bad --> show warning smartWarning = createSmartWarning(50, 10, ((u16StdscrX) - (int)(u16StdscrX / 2) + 35), (int)(u16StdscrY / 2) - 5, it->getPath(), it->getPowerOnHours(), it->getPowerCycles(), it->getErrorCount(), it->getTemperature()); wrefresh(smartWarning); } } stringstream stream; switch (it->state) { case Drive::SHRED_ACTIVE: stream << fixed << setprecision(3) << (it->getTaskPercentage()); sState = "Shredding: " + stream.str() + "%"; it->calculateTaskDuration(); sTime = this->formatTimeDuration(it->getTaskDuration()); sSpeed = this->formatSpeed(it->sShredSpeed.u32ShredTimeDelta, it->sShredSpeed.ulWrittenBytes); break; case Drive::CHECK_ACTIVE: stream << fixed << setprecision(3) << (it->getTaskPercentage()); sState = "Checking: " + stream.str() + "%"; it->calculateTaskDuration(); sTime = this->formatTimeDuration(it->getTaskDuration()); sSpeed = this->formatSpeed(it->sShredSpeed.u32ShredTimeDelta, it->sShredSpeed.ulWrittenBytes); break; case Drive::DELETE_ACTIVE: sState = "Deleting ..."; it->calculateTaskDuration(); sTime = this->formatTimeDuration(it->getTaskDuration()); break; case Drive::NONE: case Drive::SHRED_SELECTED: case Drive::DELETE_SELECTED: if (it->bWasDeleted) { sState = "DELETED"; // mark drive as deleted previously } if (it->bWasShredded) { if (it->bWasChecked) { // drive was also checked after shredding sState = "SHREDDED & CHECKED"; // mark drive as shredded previously and optional checked } else { // shredded and not checked yet sState = "SHREDDED"; // mark drive as shredded previously } sTime = this->formatTimeDuration(it->getTaskDuration()); } #ifdef ZERO_CHECK if (bSelectedEntry && it->bWasChecked && (it->u32DriveChecksumAfterShredding != 0U)) { dialog = createZeroChecksumWarning(70, 16, ((u16StdscrX) - (int)(u16StdscrX / 2) - 20), (int)(u16StdscrY / 2) - 8, it->getPath(), it->getModelFamily(), it->getModelName(), it->getSerial(), it->u32DriveChecksumAfterShredding); wrefresh(dialog); } #endif break; case Drive::FROZEN: stream << fixed << setprecision(3) << (it->getTaskPercentage()); #ifdef FROZEN_ALERT if (bSelectedEntry) { dialog = createFrozenWarning(70, 16, ((u16StdscrX) - (int)(u16StdscrX / 2) - 20), (int)(u16StdscrY / 2) - 8, it->getPath(), it->getModelFamily(), it->getModelName(), it->getSerial(), stream.str() + "%"); wrefresh(dialog); } #endif sState = "FROZEN " + stream.str() + "%"; // mark drive as frozen and reached progress break; default: break; } uint16_t u16StartOffsetY = (2 * (u8Index)); WINDOW *tmp = createEntryWindow((int)(u16StdscrX * (float)(3.0 / 7.0) - 2), 2, 3, u16StartOffsetY + 2, (distance(plistDrives->begin(), it) + 1), sModelFamily, sSerial, sCapacity, sState, sTime, sSpeed, sTemp, bSelectedEntry); wrefresh(tmp); u8Index++; } // end loop though drives if (plistDrives->size() == 0) { // no selected drive present Logger::logThis()->warning("no selected drive present"); struct MenuState menustate; menustate.bAbort = false; menustate.bConfirmDelete = false; menustate.bConfirmShred = false; menustate.bDelete = false; menustate.bShred = false; detailview = overwriteDetailViewWindow((u16StdscrX) - ((int)(u16StdscrX * (float)(3.0 / 7.0))) - 7, (u16StdscrY - 15), (int)(u16StdscrX * (float)(3.0 / 7.0) + 5)); wrefresh(detailview); menuview = createMenuView(((int)(u16StdscrX * (float)(2.0 / 7.0))) - 3, 12, (int)(u16StdscrX * (float)(3.0 / 7.0) + 5), (u16StdscrY - 13), menustate); wrefresh(menuview); } mxUIrefresh.unlock(); } enum TUI::UserInput TUI::readUserInput() { int ch = wgetch(stdscr); switch (ch) { case KEY_UP: return TUI::UserInput::UpKey; break; case KEY_DOWN: return TUI::UserInput::DownKey; break; case 10: return TUI::UserInput::Enter; break; case 27: return TUI::UserInput::ESC; break; case 'a': return TUI::UserInput::Abort; break; case 'd': return TUI::UserInput::Delete; break; case 's': return TUI::UserInput::Shred; break; case 'S': return TUI::UserInput::ShredAll; break; default: return TUI::UserInput::Undefined; break; } return TUI::UserInput::Undefined; } void TUI::centerTitle(WINDOW *pwin, const char *title) { int x, maxX, stringSize; getmaxyx(pwin, maxX, maxX); stringSize = 4 + strlen(title); x = (maxX - stringSize) / 2; mvwaddch(pwin, 0, x, ACS_RTEE); waddch(pwin, ' '); waddstr(pwin, title); waddch(pwin, ' '); waddch(pwin, ACS_LTEE); } /* left window that contains the drive entries */ WINDOW *TUI::createOverViewWindow(int iXSize, int iYSize) { WINDOW *newWindow; newWindow = newwin(iYSize, iXSize, 1, 2); wbkgd(newWindow, COLOR_PAIR(COLOR_AREA_OVERVIEW)); box(newWindow, ACS_VLINE, ACS_HLINE); centerTitle(newWindow, "Detected Drives"); return newWindow; } WINDOW *TUI::createDetailViewWindow(int iXSize, int iYSize, int iXStart, Drive drive) { WINDOW *newWindow; newWindow = newwin(iYSize, iXSize, 1, iXStart); wbkgd(newWindow, COLOR_PAIR(COLOR_AREA_DETAIL)); box(newWindow, ACS_VLINE, ACS_HLINE); string title = "Selected Drive: " + drive.getModelName() + " " + drive.sCapacityToText(); centerTitle(newWindow, title.c_str()); string sPath = "Path: " + drive.getPath(); string sModelFamily = "ModelFamily: " + drive.getModelFamily(); string sModelName = "ModelName: " + drive.getModelName(); string sCapacity = "Capacity: " + drive.sCapacityToText(); string sSerial = "Serial: " + drive.getSerial(); string sPowerOnHours = "PowerOnHours: " + drive.sPowerOnHoursToText(); string sPowerCycle = "PowerCycle: " + drive.sPowerCyclesToText(); string sErrorCount = "ErrorCount: " + drive.sErrorCountToText(); uint16_t u16Line = 2; mvwaddstr(newWindow, u16Line++, 3, sPath.c_str()); mvwaddstr(newWindow, u16Line++, 3, sModelFamily.c_str()); mvwaddstr(newWindow, u16Line++, 3, sModelName.c_str()); mvwaddstr(newWindow, u16Line++, 3, sCapacity.c_str()); mvwaddstr(newWindow, u16Line++, 3, sSerial.c_str()); attroff(COLOR_PAIR(COLOR_AREA_DETAIL)); mvwaddstr(newWindow, u16Line++, 3, sPowerOnHours.c_str()); mvwaddstr(newWindow, u16Line++, 3, sPowerCycle.c_str()); mvwaddstr(newWindow, u16Line++, 3, sErrorCount.c_str()); return newWindow; } WINDOW *TUI::overwriteDetailViewWindow(int iXSize, int iYSize, int iXStart) { WINDOW *newWindow; newWindow = newwin(iYSize, iXSize, 1, iXStart); wbkgd(newWindow, COLOR_PAIR(COLOR_AREA_DETAIL)); box(newWindow, ACS_VLINE, ACS_HLINE); string title = "About this tool"; centerTitle(newWindow, title.c_str()); string sLine01 = "reHDD - hard drive refurbishing tool"; string sLine02 = "Version: " + string(REHDD_VERSION); string sLine03 = "Available under GPL 3.0"; string sLine04 = "https://git.mosad.xyz/localhorst/reHDD"; string sLine05 = "Delete: Wipe format table - this is NOT secure"; string sLine06 = "Shred: Overwrite drive " + to_string(SHRED_ITERATIONS) + " iterations - this is secure"; uint16_t u16Line = 5; mvwaddstr(newWindow, u16Line++, (iXSize / 2) - (sLine01.size() / 2), sLine01.c_str()); mvwaddstr(newWindow, u16Line++, (iXSize / 2) - (sLine02.size() / 2), sLine02.c_str()); u16Line++; mvwaddstr(newWindow, u16Line++, (iXSize / 2) - (sLine03.size() / 2), sLine03.c_str()); mvwaddstr(newWindow, u16Line++, (iXSize / 2) - (sLine04.size() / 2), sLine04.c_str()); u16Line++; mvwaddstr(newWindow, u16Line++, (iXSize / 2) - (sLine05.size() / 2), sLine05.c_str()); mvwaddstr(newWindow, u16Line++, (iXSize / 2) - (sLine06.size() / 2), sLine06.c_str()); attroff(COLOR_PAIR(COLOR_AREA_DETAIL)); return newWindow; } WINDOW *TUI::createEntryWindow(int iXSize, int iYSize, int iXStart, int iYStart, int iListIndex, string sModelFamily, string sSerial, string sCapacity, string sState, string sTime, string sSpeed, string sTemp, bool bSelected) { WINDOW *newWindow; newWindow = newwin(iYSize, iXSize, iYStart, iXStart); if (!bSelected) { // entry is NOT selected if (iListIndex % 2 == 0) { // even attron(COLOR_PAIR(COLOR_AREA_ENTRY_EVEN)); wbkgd(newWindow, COLOR_PAIR(COLOR_AREA_ENTRY_EVEN)); } else { // odd attron(COLOR_PAIR(COLOR_AREA_ENTRY_ODD)); wbkgd(newWindow, COLOR_PAIR(COLOR_AREA_ENTRY_ODD)); } } else { // entry IS selected attron(COLOR_PAIR(COLOR_AREA_ENTRY_SELECTED)); wbkgd(newWindow, COLOR_PAIR(COLOR_AREA_ENTRY_SELECTED)); } // box(newWindow, ACS_VLINE, ACS_HLINE); // index number mvwaddstr(newWindow, 0, 1, to_string(iListIndex).c_str()); /* 70 chars in x-axis line:01 0: space 1: index number 2: space 3-35: ModelFamily 36: space 37-43: Capacity 44: space 47-49: Temp line:02 0-2: space 3-31: Serial 32: space 33-45: Speed 46: space 47-58: Time 59: space 60-70: State (but right side aligned) */ vTruncateText(&sModelFamily, 32); mvwaddstr(newWindow, 0, 3, sModelFamily.c_str()); mvwaddstr(newWindow, 0, 37, sCapacity.c_str()); mvwaddstr(newWindow, 0, 47, sTemp.c_str()); vTruncateText(&sSerial, 28); mvwaddstr(newWindow, 1, 3, sSerial.c_str()); mvwaddstr(newWindow, 1, 33, sSpeed.c_str()); mvwaddstr(newWindow, 1, 47, sTime.c_str()); mvwaddstr(newWindow, 1, iXSize - sState.length() - 2, sState.c_str()); return newWindow; } WINDOW *TUI::createSystemStats(int iXSize, int iYSize, int iXStart, int iYStart) { WINDOW *newWindow; newWindow = newwin(iYSize, iXSize, iYStart, iXStart); wbkgd(newWindow, COLOR_PAIR(COLOR_AREA_OVERVIEW)); box(newWindow, ACS_VLINE, ACS_HLINE); centerTitle(newWindow, "System"); time_t rawtime; struct tm *timeinfo; char buffer[80]; time(&rawtime); timeinfo = localtime(&rawtime); strftime(buffer, sizeof(buffer), "Date: %d-%m-%Y Time: %H:%M", timeinfo); string time(buffer); string sLine01 = "reHDD - hard drive refurbishing tool"; string sLine02 = "Version: " + string(REHDD_VERSION); string sLine03 = "Build time: "; sLine03.append(__DATE__); sLine03.append(" "); sLine03.append(__TIME__); string sLine04 = "Available under GPL 3.0"; string sLine05 = "https://git.mosad.xyz/localhorst/reHDD"; uint16_t u16Line = 2; mvwaddstr(newWindow, u16Line++, (iXSize / 2) - (sLine01.size() / 2), sLine01.c_str()); u16Line++; mvwaddstr(newWindow, u16Line++, (iXSize / 2) - (sLine01.size() / 2), sLine02.c_str()); mvwaddstr(newWindow, u16Line++, (iXSize / 2) - (sLine01.size() / 2), sLine03.c_str()); mvwaddstr(newWindow, u16Line++, (iXSize / 2) - (sLine01.size() / 2), sLine04.c_str()); mvwaddstr(newWindow, u16Line++, (iXSize / 2) - (sLine01.size() / 2), sLine05.c_str()); u16Line++; mvwaddstr(newWindow, u16Line++, (iXSize / 2) - (sLine01.size() / 2), time.c_str()); return newWindow; } WINDOW *TUI::createMenuView(int iXSize, int iYSize, int iXStart, int iYStart, struct MenuState menustate) { WINDOW *newWindow; newWindow = newwin(iYSize, iXSize, iYStart, iXStart); wbkgd(newWindow, COLOR_PAIR(COLOR_AREA_OVERVIEW)); box(newWindow, ACS_VLINE, ACS_HLINE); centerTitle(newWindow, "Controls"); uint16_t u16Line = 4; if (menustate.bAbort) { string sLineTmp = "Press a for Abort"; mvwaddstr(newWindow, u16Line++, (iXSize / 2) - (sLineTmp.size() / 2), sLineTmp.c_str()); u16Line++; } if (menustate.bShred) { string sLineTmp = "Press s for Shred (S for all drives)"; mvwaddstr(newWindow, u16Line++, (iXSize / 2) - (sLineTmp.size() / 2), sLineTmp.c_str()); u16Line++; } if (menustate.bDelete) { string sLineTmp = "Press d for Delete"; mvwaddstr(newWindow, u16Line++, (iXSize / 2) - (sLineTmp.size() / 2), sLineTmp.c_str()); } return newWindow; } WINDOW *TUI::createDialog(int iXSize, int iYSize, int iXStart, int iYStart, string task, string optionA, string optionB) { WINDOW *newWindow; newWindow = newwin(iYSize, iXSize, iYStart, iXStart); wbkgd(newWindow, COLOR_PAIR(COLOR_AREA_ENTRY_SELECTED)); box(newWindow, ACS_VLINE, ACS_HLINE); centerTitle(newWindow, task.c_str()); uint16_t u16Line = 3; mvwaddstr(newWindow, u16Line++, (iXSize / 2) - (optionA.size() / 2), optionA.c_str()); u16Line++; mvwaddstr(newWindow, u16Line++, (iXSize / 2) - (optionB.size() / 2), optionB.c_str()); return newWindow; } WINDOW *TUI::createFrozenWarning(int iXSize, int iYSize, int iXStart, int iYStart, string sPath, string sModelFamily, string sModelName, string sSerial, string sProgress) { WINDOW *newWindow; newWindow = newwin(iYSize, iXSize, iYStart, iXStart); wbkgd(newWindow, COLOR_PAIR(COLOR_AREA_ENTRY_SELECTED)); box(newWindow, ACS_VLINE, ACS_HLINE); string sHeader = "Drive " + sPath + " is frozen"; string sLine01 = "Please detach this drive and check it manually:"; string sShredState = "Shredding stopped after " + sProgress; string sLinePath = "Path: " + sPath; string sLineModelFamlily = "ModelFamily: " + sModelFamily; string sLineModelName = "ModelName: " + sModelName; string sLineSerial = "Serial: " + sSerial; string sLine02 = "reHDD was not able to write data to the drive."; string sLine03 = "This can be caused by an malfunctioning drive."; centerTitle(newWindow, sHeader.c_str()); uint16_t u16Line = 2; mvwaddstr(newWindow, u16Line++, 3, sLine01.c_str()); u16Line++; mvwaddstr(newWindow, u16Line++, 3, sLinePath.c_str()); mvwaddstr(newWindow, u16Line++, 3, sLineModelFamlily.c_str()); mvwaddstr(newWindow, u16Line++, 3, sLineModelName.c_str()); mvwaddstr(newWindow, u16Line++, 3, sLineSerial.c_str()); u16Line++; mvwaddstr(newWindow, u16Line++, 3, sLine02.c_str()); mvwaddstr(newWindow, u16Line++, 3, sLine03.c_str()); mvwaddstr(newWindow, u16Line++, 3, sShredState.c_str()); return newWindow; } WINDOW *TUI::createZeroChecksumWarning(int iXSize, int iYSize, int iXStart, int iYStart, string sPath, string sModelFamily, string sModelName, string sSerial, uint32_t u32Checksum) { WINDOW *newWindow; newWindow = newwin(iYSize, iXSize, iYStart, iXStart); wbkgd(newWindow, COLOR_PAIR(COLOR_AREA_ENTRY_SELECTED)); box(newWindow, ACS_VLINE, ACS_HLINE); string sHeader = "Drive " + sPath + " is NOT successfully shredded!"; string sLine01 = "Please detach this drive and check it manually:"; string sShredChecksum = "After shredding the checksum was: " + to_string(u32Checksum); string sLinePath = "Path: " + sPath; string sLineModelFamily = "ModelFamily: " + sModelFamily; string sLineModelName = "ModelName: " + sModelName; string sLineSerial = "Serial: " + sSerial; string sLine02 = "reHDD was not able to write zero into every byte on the drive."; string sLine03 = "This can be caused by an malfunctioning drive."; centerTitle(newWindow, sHeader.c_str()); uint16_t u16Line = 2; mvwaddstr(newWindow, u16Line++, 3, sLine01.c_str()); u16Line++; mvwaddstr(newWindow, u16Line++, 3, sLinePath.c_str()); mvwaddstr(newWindow, u16Line++, 3, sLineModelFamily.c_str()); mvwaddstr(newWindow, u16Line++, 3, sLineModelName.c_str()); mvwaddstr(newWindow, u16Line++, 3, sLineSerial.c_str()); u16Line++; mvwaddstr(newWindow, u16Line++, 3, sLine02.c_str()); mvwaddstr(newWindow, u16Line++, 3, sLine03.c_str()); mvwaddstr(newWindow, u16Line++, 3, sShredChecksum.c_str()); return newWindow; } string TUI::formatTimeDuration(time_t u32Duration) { std::ostringstream out; int dy = (int)((u32Duration) / 86400); int hr = (int)(((u32Duration) / 3600) % 24); int min = ((int)((u32Duration) / 60)) % 60; int sec = (int)((u32Duration) % 60); char s[25]; sprintf(s, "%02d:%02d:%02d:%02d", dy, hr, min, sec); out << s; return out.str(); } string TUI::formatSpeed(time_t u32ShredTimeDelta, unsigned long ulWrittenBytes) { std::ostringstream out; double dDeltaSec = ((double)((u32ShredTimeDelta) / 1000000000.0)); // convert nano in sec double speed = ((ulWrittenBytes / 1000000.0) / dDeltaSec); char s[25]; sprintf(s, "%0.2lf MB/s", speed); out << s; return out.str(); } void TUI::vTruncateText(string *psText, uint16_t u16MaxLenght) { if (psText->length() > u16MaxLenght) { psText->resize(u16MaxLenght - 3); *psText = *psText + "..."; } } void TUI::displaySelectedDrive(Drive drive, int stdscrX, int stdscrY) { struct MenuState menustate; menustate.bAbort = false; menustate.bConfirmDelete = false; menustate.bConfirmShred = false; menustate.bDelete = false; menustate.bShred = false; // set menustate based on drive state switch (drive.state) { case Drive::NONE: // no task running or selected for this drive menustate.bShred = true; menustate.bDelete = true; break; case Drive::DELETE_ACTIVE: // delete task running for this drive menustate.bAbort = true; break; case Drive::SHRED_ACTIVE: // shred task running for this drive menustate.bAbort = true; break; case Drive::CHECK_ACTIVE: // check task running for this drive menustate.bAbort = true; break; case Drive::DELETE_SELECTED: // delete task selected for this drive menustate.bConfirmDelete = true; break; case Drive::SHRED_SELECTED: // shred task selected for this drive menustate.bConfirmShred = true; break; default: break; } detailview = createDetailViewWindow((stdscrX) - ((int)(stdscrX * (float)(3.0 / 7.0))) - 7, (stdscrY - 15), (int)(stdscrX * (float)(3.0 / 7.0) + 5), drive); wrefresh(detailview); menuview = createMenuView(((int)(stdscrX * (float)(2.0 / 7.0))) - 3, 12, (int)(stdscrX * (float)(3.0 / 7.0) + 5), (stdscrY - 13), menustate); wrefresh(menuview); if (menustate.bConfirmShred == true) { dialog = createDialog(40, 10, ((stdscrX) - (int)(stdscrX / 3) - 7) - (int)((stdscrX / 3) + 5) / 2, (int)(stdscrY / 2) - 5, "Confirm SHRED", "Press ENTER for SHRED", "Press ESC for cancel"); wrefresh(dialog); } else if (menustate.bConfirmDelete == true) { dialog = createDialog(40, 10, ((stdscrX) - (int)(stdscrX / 3) - 7) - (int)((stdscrX / 3) + 5) / 2, (int)(stdscrY / 2) - 5, "Confirm DELETE", "Press ENTER for DELETE", "Press ESC for cancel"); wrefresh(dialog); } else { delwin(dialog); } } WINDOW *TUI::createSmartWarning(int iXSize, int iYSize, int iXStart, int iYStart, string sPath, uint32_t u32PowerOnHours, uint32_t u32PowerCycles, uint32_t u32ErrorCount, uint32_t u32Temperature) { WINDOW *newWindow; newWindow = newwin(iYSize, iXSize, iYStart, iXStart); wbkgd(newWindow, COLOR_PAIR(COLOR_AREA_ENTRY_SELECTED)); box(newWindow, ACS_VLINE, ACS_HLINE); string sHeader = "Drive " + sPath + " is suspicious"; string sLine01 = "Please evaluate this drive carefully."; centerTitle(newWindow, sHeader.c_str()); uint16_t u16Line = 2; mvwaddstr(newWindow, u16Line++, (iXSize / 2) - (sLine01.size() / 2), sLine01.c_str()); u16Line++; if (u32PowerOnHours > WORSE_HOURS) { string sLineTmp = "Operating hours exceeded " + to_string(WORSE_HOURS) + " hours: " + to_string(u32PowerOnHours); mvwaddstr(newWindow, u16Line++, (iXSize / 2) - (sLine01.size() / 2), sLineTmp.c_str()); u16Line++; } if (u32PowerCycles > WORSE_POWERUP) { string sLineTmp = "Power-on exceeded " + to_string(WORSE_POWERUP) + " cycles: " + to_string(u32PowerCycles); mvwaddstr(newWindow, u16Line++, (iXSize / 2) - (sLine01.size() / 2), sLineTmp.c_str()); u16Line++; } if (u32ErrorCount > 0) { string sLineTmp = "S.M.A.R.T. errors detected: " + to_string(u32ErrorCount); mvwaddstr(newWindow, u16Line++, (iXSize / 2) - (sLine01.size() / 2), sLineTmp.c_str()); u16Line++; } if (u32Temperature >= WORSE_TEMPERATURE) { string sLineTmp = "Drive too hot: " + to_string(u32Temperature) + " C"; mvwaddstr(newWindow, u16Line++, (iXSize / 2) - (sLine01.size() / 2), sLineTmp.c_str()); } return newWindow; }