/** * @file tui.cpp * @brief display user interface * @author hendrik schutter * @date 03.08.2020 */ #include "../include/reHDD.h" 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"); 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, COLOR_BLACK, COLOR_WHITE); init_pair(COLOR_AREA_ENTRY_SELECTED, COLOR_BLACK, 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 "); } void TUI::updateTUI(vector * pvecDrives, uint8_t u8SelectedEntry) { int stdscrX, stdscrY; getmaxyx(stdscr, stdscrY, stdscrX); init_pair(COLOR_AREA_STDSCR,COLOR_WHITE, COLOR_BLUE); wbkgd(stdscr, COLOR_PAIR(COLOR_AREA_STDSCR)); refresh(); overview=createOverViewWindow((int)(stdscrX/3), (stdscrY-15)); wrefresh(overview); systemview=createSystemStats((int)(stdscrX/3), 10, (stdscrY-11)); wrefresh(systemview); delwin(detailview); vector ::iterator it; for (it = pvecDrives->begin(); it != pvecDrives->end(); ++it) { string sModelFamily = it->getModelFamily(); string sModelName = it->getModelName(); string sCapacity = it->sCapacityToText(); string sState = " "; bool bSelectedEntry = false; if(u8SelectedEntry == (it - pvecDrives->begin())) { bSelectedEntry = true; //mark this drive in entries list displaySelectedDrive(pvecDrives->at(u8SelectedEntry), stdscrX, stdscrY); } switch (it->state) { case Drive::SHRED_ACTIVE: sState = "Shredding: " + to_string(it->getTaskPercentage()) + "%"; break; case Drive::DELETE_ACTIVE: sState = "Deleting ..."; break; case Drive::NONE: case Drive::SHRED_SELECTED: case Drive::DELETE_SELECTED: if (it->bWasDeleteted) { sState = "DELETED"; //mark drive as deleted previously } if (it->bWasShredded) { sState = "SHREDDED"; //mark drive as shreded previously, overwrite if deleted } break; default: break; } WINDOW * tmp = createEntryWindow( ((int)(stdscrX/3) - 2), 5, 3, (5* (it - pvecDrives->begin()) )+3, sModelFamily, sModelName, sCapacity, sState, bSelectedEntry); wrefresh(tmp); }//end loop though drives if(pvecDrives->size() == 0) { //no selected drive present struct MenuState menustate; menustate.bAbort = false; menustate.bConfirmDelete = false; menustate.bConfirmShred = false; menustate.bDelete = false; menustate.bShred = false; menuview=createMenuView(((stdscrX)-(int)(stdscrX/3)-7), 10, (int)(stdscrX/3)+5,(stdscrY-11), menustate); wrefresh(menuview); detailview=overwriteDetailViewWindow(((stdscrX)-(int)(stdscrX/3)-7), (stdscrY-15), (int)(stdscrX/3)+5); wrefresh(detailview); } } 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; 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); } WINDOW* TUI::createOverViewWindow( int iXSize, int iYSize) { WINDOW *newWindow; newWindow = newwin(iYSize, iXSize, 2, 2); wbkgd(newWindow, COLOR_PAIR(COLOR_AREA_OVERVIEW)); box(newWindow, ACS_VLINE, ACS_HLINE); centerTitle(newWindow, "Detected Drives"); keypad(newWindow, TRUE); return newWindow; } WINDOW* TUI::createDetailViewWindow( int iXSize, int iYSize, int iXStart, Drive drive) { WINDOW *newWindow; newWindow = newwin(iYSize, iXSize, 2, 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 sModelFamlily = "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, sModelFamlily.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)); if(drive.getPowerOnHours() >= WORSE_HOURS) { mvwaddstr(newWindow,u16Line++, 3, "------------> WARNING: OPERATING HOURS <-----------"); mvwaddstr(newWindow,u16Line++, 3, sPowerOnHours.c_str()); mvwaddstr(newWindow,u16Line++, 3, "---------------------------------------------------"); } else { mvwaddstr(newWindow,u16Line++, 3, sPowerOnHours.c_str()); } if(drive.getPowerCycles() >= WORSE_POWERUP) { mvwaddstr(newWindow,u16Line++, 3, "------------> WARNING: POWER-ON <------------------"); mvwaddstr(newWindow,u16Line++, 3, sPowerCycle.c_str()); mvwaddstr(newWindow,u16Line++, 3, "---------------------------------------------------"); } else { mvwaddstr(newWindow,u16Line++, 3, sPowerCycle.c_str()); } if(drive.getErrorCount() > 0) { mvwaddstr(newWindow,u16Line++, 3, "------------> WARNING: S.M.A.R.T ERROR <-----------"); mvwaddstr(newWindow,u16Line++, 3, sErrorCount.c_str()); mvwaddstr(newWindow,u16Line++, 3, "---------------------------------------------------"); } else { mvwaddstr(newWindow,u16Line++, 3, sErrorCount.c_str()); } keypad(newWindow, TRUE); return newWindow; } WINDOW* TUI::overwriteDetailViewWindow( int iXSize, int iYSize, int iXStart) { WINDOW *newWindow; newWindow = newwin(iYSize, iXSize, 2, 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 = "Path: NextLine "; string sLine02 = "Path: NextLine "; string sLine03 = "Path: NextLine "; string sLine04 = "Path: NextLine "; string sLine05 = "Path: NextLine "; string sLine06 = "Path: NextLine "; string sLine07 = "Path: NextLine "; 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()); mvwaddstr(newWindow,u16Line++, (iXSize/2)-(sLine03.size()/2), sLine03.c_str()); mvwaddstr(newWindow,u16Line++, (iXSize/2)-(sLine04.size()/2), sLine04.c_str()); mvwaddstr(newWindow,u16Line++, (iXSize/2)-(sLine05.size()/2), sLine05.c_str()); mvwaddstr(newWindow,u16Line++, (iXSize/2)-(sLine06.size()/2), sLine06.c_str()); mvwaddstr(newWindow,u16Line++, (iXSize/2)-(sLine07.size()/2), sLine07.c_str()); attroff(COLOR_PAIR(COLOR_AREA_DETAIL)); keypad(newWindow, TRUE); return newWindow; } WINDOW* TUI::createEntryWindow(int iXSize, int iYSize, int iXStart, int iYStart, string sModelFamily, string sModelName, string sCapacity, string sState, bool bSelected) { WINDOW *newWindow; newWindow = newwin(iYSize, iXSize, iYStart, iXStart); if(!bSelected) { // entry is NOT selected attron(COLOR_PAIR(COLOR_AREA_ENTRY)); wbkgd(newWindow, COLOR_PAIR(COLOR_AREA_ENTRY)); } else { // entry IS selected attron(COLOR_PAIR(COLOR_AREA_ENTRY)); wbkgd(newWindow, COLOR_PAIR(COLOR_AREA_ENTRY_SELECTED)); } box(newWindow, ACS_VLINE, ACS_HLINE); mvwaddstr(newWindow,1, 1, sModelFamily.c_str()); mvwaddstr(newWindow,2, 1, sModelName.c_str()); mvwaddstr(newWindow,3, 1, sCapacity.c_str()); mvwaddstr(newWindow,2, iXSize-sState.length()-5, sState.c_str()); keypad(newWindow, TRUE); return newWindow; } WINDOW* TUI::createSystemStats(int iXSize, int iYSize, int iYStart) { WINDOW *newWindow; newWindow = newwin(iYSize, iXSize, iYStart, 2); 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); mvwaddstr(newWindow,2, 2, time.c_str()); keypad(newWindow, TRUE); 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 = 2; if(menustate.bAbort) { mvwaddstr(newWindow,u16Line++, 3, "Press A for Abort"); } if(menustate.bShred) { mvwaddstr(newWindow,u16Line++, 3, "Press S for Shred"); } if(menustate.bDelete) { mvwaddstr(newWindow,u16Line++, 3, "Press D for Delete"); } keypad(newWindow, TRUE); 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 = 2; mvwaddstr(newWindow,u16Line++, 3, optionA.c_str()); mvwaddstr(newWindow,u16Line++, 3, optionB.c_str()); keypad(newWindow, TRUE); return newWindow; } 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::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/3)-7), (stdscrY-15), (int)(stdscrX/3)+5, drive); wrefresh(detailview); menuview=createMenuView(((stdscrX)-(int)(stdscrX/3)-7), 10, (int)(stdscrX/3)+5,(stdscrY-11), menustate); wrefresh(menuview); if(menustate.bConfirmShred == true) { dialog=createDialog(70, 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(70, 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); } }