void Terminal::SetPrompt(const char *input_){ prompt = input_; //Calculate the offset. //This is long winded as we want to ignore the escape sequences offset = 0; size_t pos = 0, lastPos = 0; while ((pos = prompt.find("\033[",lastPos)) != std::string::npos) { //Increase offset by number characters prior to escape offset += pos - lastPos; lastPos = pos; //Identify which escape code we found. //First try reset code then loop through other codes if (pos == prompt.find(TermColors::Reset,pos)) { lastPos += std::string(TermColors::Reset).length(); } else { for(auto it=attrMap.begin(); it!= attrMap.end(); ++it) { //If the attribute is at the same position then we found this attribute and we turn it on if (pos == prompt.find(it->first,pos)) { //Iterate position to suppress printing the escape string lastPos += std::string(it->first).length(); break; } } } } offset += prompt.length() - lastPos; update_cursor_(); print(input_window,prompt.c_str()); refresh_(); }
// Force a character to the input screen void Terminal::in_char_(const char input_){ cursX++; //If in insert mode we overwite the character otherwise insert it. if (cmd.GetInsertMode()) waddch(input_window, input_); else winsch(input_window, input_); update_cursor_(); refresh_(); }
void Terminal::clear_(){ for(int start = cmd.GetSize() + offset; start >= offset; start--){ wmove(input_window, 0, start); wdelch(input_window); } cmd.Clear(); cursX = offset; update_cursor_(); refresh_(); }
void Terminal::Initialize(){ if(init){ return; } original = std::cout.rdbuf(); // Back-up cout's streambuf pbuf = stream.rdbuf(); // Get stream's streambuf std::cout.flush(); std::cout.rdbuf(pbuf); // Assign streambuf to cout main = initscr(); if(main == NULL ){ // Attempt to initialize ncurses std::cout.rdbuf(original); // Restore cout's original streambuf fprintf(stderr, " Error: failed to initialize ncurses!\n"); } else{ getmaxyx(stdscr, _winSizeY, _winSizeX); output_window = newpad(_scrollbackBufferSize, _winSizeX); input_window = newpad(1, _winSizeX); wmove(output_window, _scrollbackBufferSize-1, 0); // Set the output cursor at the bottom so that new text will scroll up if (halfdelay(5) == ERR) { // Timeout after 5/10 of a second std::cout << "WARNING: Unable to set terminal blocking half delay to 0.5s!\n"; std::cout << "\tThis will increase CPU usage in the command thread.\n"; if (nodelay(input_window, true) == ERR) { //Disable the blocking timeout. std::cout << "ERROR: Unable to remove terminal blocking!\n"; std::cout << "\tThe command thread will be locked until a character is entered. This will reduce functionality of terminal status bar and timeout.\n"; } } keypad(input_window, true); // Capture special keys noecho(); // Turn key echoing off scrollok(output_window, true); scrollok(input_window, true); if (NCURSES_MOUSE_VERSION > 0) { mousemask(ALL_MOUSE_EVENTS,NULL); mouseinterval(0); } init = true; offset = 0; // Set the position of the physical cursor cursX = 0; cursY = _winSizeY-1; update_cursor_(); refresh_(); init_colors_(); } setup_signal_handlers(); }
/**Take the list of matching tab complete values and output resulting tab completion. * If the list is empty nothing happens, if a unique value is given the command is completed. If there are multiple * matches the common part of the matches is determined and printed to the input. If there is no common part of the * matches and the tab key has been pressed twice the list of matches is printed for the user to decide. * * \param[in] matches A vector of strings of trailing characters matching the current command. */ void Terminal::TabComplete(std::vector<std::string> matches) { //No tab complete matches so we do nothing. if (matches.size() == 0) { return; } //A unique match so we extend the command with completed text else if (matches.size() == 1) { if (matches.at(0).find("/") == std::string::npos && (unsigned int) (cursX - offset) == cmd.Get().length()) { matches.at(0).append(" "); } cmd.Insert(cursX - offset, matches.at(0).c_str()); waddstr(input_window, cmd.Get().substr(cursX - offset).c_str()); cursX += matches.at(0).length(); text_length += matches.at(0).length(); } else { //Fill out the matching part std::string commonStr = matches.at(0); for (auto it=matches.begin()+1;it!=matches.end() && !commonStr.empty();++it) { while (!commonStr.empty()) { if ((*it).find(commonStr) == 0) break; commonStr.erase(commonStr.length() - 1); } } if (!commonStr.empty()) { cmd.Insert(cursX - offset, commonStr.c_str()); waddstr(input_window, cmd.Get().substr(cursX - offset).c_str()); cursX += commonStr.length(); text_length += commonStr.length(); } //Display the options else if (tabCount > 1) { //Compute the header position size_t headerPos = cmd.Get().find_last_of(" /",cursX - offset - 1); std::string header; if (headerPos == std::string::npos) header = cmd.Get(); else header = cmd.Get().substr(headerPos+1,cursX - offset - 1 - headerPos); std::cout << prompt.c_str() << cmd.Get() << "\n"; for (auto it=matches.begin();it!=matches.end();++it) { std::cout << header << (*it) << "\t"; } std::cout << "\n"; } } update_cursor_(); refresh_(); }
/**Creates a status window and the refreshes the output. Takes an optional number of lines, defaulted to 1. * * \param[in] numLines Vertical size of status window. */ void Terminal::AddStatusWindow(unsigned short numLines) { _statusWindowSize = numLines; //Create the new status pad. status_window = newpad(_statusWindowSize, _winSizeX); for (int i=0;i<numLines;i++) statusStr.push_back(std::string("")); //Update the cursor position cursY = _winSizeY - _statusWindowSize - 1; update_cursor_(); //Refresh the screen refresh_(); }
void Terminal::Initialize(){ if(init){ return; } original = std::cout.rdbuf(); // Back-up cout's streambuf pbuf = stream.rdbuf(); // Get stream's streambuf std::cout.flush(); std::cout.rdbuf(pbuf); // Assign streambuf to cout main = initscr(); if(main == NULL ){ // Attempt to initialize ncurses std::cout.rdbuf(original); // Restore cout's original streambuf fprintf(stderr, " Error: failed to initialize ncurses!\n"); } else{ getmaxyx(stdscr, _winSizeY, _winSizeX); output_window = newpad(_scrollbackBufferSize, _winSizeX); input_window = newpad(1, _winSizeX); wmove(output_window, _scrollbackBufferSize-1, 0); // Set the output cursor at the bottom so that new text will scroll up halfdelay(5); // Timeout after 5/10 of a second keypad(input_window, true); // Capture special keys noecho(); // Turn key echoing off scrollok(output_window, true); scrollok(input_window, true); if (NCURSES_MOUSE_VERSION > 0) { mousemask(ALL_MOUSE_EVENTS,NULL); mouseinterval(0); } init = true; text_length = 0; offset = 0; // Set the position of the physical cursor cursX = 0; cursY = _winSizeY-1; update_cursor_(); refresh_(); init_colors_(); } setup_signal_handlers(); }
std::string Terminal::GetCommand(const int &prev_cmd_return_/*=0*/){ std::string output = ""; sclock::time_point commandRequestTime = sclock::now(); sclock::time_point currentTime; //Update status message if (status_window) { werase(status_window); print(status_window,statusStr.at(0).c_str()); } // Check for commands in the command queue. if(!prompt_user && !cmd_queue.empty()){ while(true){ output = cmd_queue.front(); cmd_queue.pop_front(); // Check for blank lines. if(!output.empty()){ break; } } from_script = true; } else{ // Get a command from the user. while(true){ if(SIGNAL_SEGFAULT){ // segmentation fault (SIGSEGV) Close(); return "_SIGSEGV_"; } if(SIGNAL_INTERRUPT){ // ctrl-c (SIGINT) SIGNAL_INTERRUPT = false; output = "CTRL_C"; break; } if(SIGNAL_TERMSTOP){ // ctrl-z (SIGTSTP) SIGNAL_TERMSTOP = false; output = "CTRL_Z"; break; } //Update status message if (status_window) { werase(status_window); print(status_window,statusStr.at(0).c_str()); } flush(); // If there is anything in the stream, dump it to the screen // Time out if there is no command within the set interval (default 0.5 s). if (commandTimeout_ > 0) { // Update the current time. currentTime = sclock::now(); std::chrono::duration<float> time_span = std::chrono::duration_cast<std::chrono::duration<float>>(currentTime - commandRequestTime); // If the timeout has passed we simply return the empty output string. if (time_span.count() > commandTimeout_) { break; } } int keypress = wgetch(input_window); // Check for internal commands if(keypress == ERR){continue;} // No key was pressed in the interval else if(keypress == 10){ // Enter key (10) std::string temp_cmd = cmd.Get(); //Reset the position in the history. commands.Reset(); if(temp_cmd != "" && temp_cmd != commands.PeekPrev()){ // Only save this command if it is different than the previous command commands.Push(temp_cmd); } output = temp_cmd; std::cout << prompt << output << "\n"; flush(); _scrollPosition = 0; clear_(); tabCount = 0; break; } else if(keypress == '\t' && enableTabComplete) { tabCount++; output = cmd.Get().substr(0,cursX - offset) + "\t"; break; } else if(keypress == 4){ // ctrl-d (EOT) output = "CTRL_D"; clear_(); tabCount = 0; break; } else if(keypress == 9){ } // Tab key (9) else if(keypress == KEY_UP){ // 259 if(commands.GetIndex() == 0){ commands.Capture(cmd.Get()); } std::string temp_cmd = commands.GetPrev(); if(temp_cmd != "NULL"){ clear_(); cmd.Set(temp_cmd); in_print_(cmd.Get().c_str()); } } else if(keypress == KEY_DOWN){ // 258 std::string temp_cmd = commands.GetNext(); if(temp_cmd != "NULL"){ clear_(); cmd.Set(temp_cmd); in_print_(cmd.Get().c_str()); } } else if(keypress == KEY_LEFT){ cursX--; } // 260 else if(keypress == KEY_RIGHT){ cursX++; } // 261 else if(keypress == KEY_PPAGE){ //Page up key scroll_(-(_winSizeY-2)); } else if(keypress == KEY_NPAGE){ //Page down key scroll_(_winSizeY-2); } else if(keypress == KEY_BACKSPACE){ // 263 wmove(input_window, 0, --cursX); wdelch(input_window); cmd.Pop(cursX - offset); } else if(keypress == KEY_DC){ // Delete character (330) //Remove character from terminal wdelch(input_window); //Remove character from cmd string cmd.Pop(cursX - offset); } else if(keypress == KEY_IC){ cmd.ToggleInsertMode(); } // Insert key (331) else if(keypress == KEY_HOME){ cursX = offset; } else if(keypress == KEY_END){ cursX = cmd.GetSize() + offset; } else if(keypress == KEY_MOUSE) { //Handle mouse events MEVENT mouseEvent; //Get information about mouse event. getmouse(&mouseEvent); switch (mouseEvent.bstate) { //Scroll up case BUTTON4_PRESSED: scroll_(-3); break; //Scroll down. (Yes the name is strange.) case REPORT_MOUSE_POSITION: scroll_(3); break; } } else if(keypress == KEY_RESIZE) { //Do nothing with the resize key } else{ in_char_((char)keypress); cmd.Put((char)keypress, cursX - offset - 1); } // Check for cursor too far to the left if(cursX < offset){ cursX = offset + cmd.GetSize(); } // Check for cursor too far to the right if(cursX > (int)(cmd.GetSize() + offset)){ cursX = cmd.GetSize() + offset; } if (keypress != ERR) tabCount = 0; update_cursor_(); refresh_(); } if(prompt_user){ // Waiting for user to specify yes or no. if(output != "yes" && output != "y"){ std::cout << prompt << "Aborting execution!\n"; cmd_queue.clear(); } prompt_user = false; return ""; } from_script = false; } // In the event of an empty command, return. if(output.empty()) return ""; // Check for system commands. std::string temp_cmd_string = output.substr(output.find_first_not_of(' '), output.find_first_of(' ')); // Strip the command from the front of the input. std::string temp_arg_string = output.substr(output.find_first_of(' ')+1, output.find_first_of('#')); // Does not ignore leading whitespace. if(temp_cmd_string.empty() || temp_cmd_string[0] == '#'){ // This is a comment line. return ""; } if(temp_cmd_string.substr(0, output.find_first_of(' ')).find('.') != std::string::npos){ if(temp_cmd_string == ".cmd"){ // Load a command script. std::string command_filename = temp_arg_string.substr(output.find_first_not_of(' ')); // Ignores leading whitespace. if(!LoadCommandFile(command_filename.c_str())){ // Failed to load command script. std::cout << prompt << "Error! Failed to load command script " << command_filename << ".\n"; } } else if(temp_cmd_string == ".echo"){ // Print something to the screen. std::cout << prompt << temp_arg_string << std::endl; } else if(temp_cmd_string == ".prompt"){ // Prompt the user with a yes/no question. std::cout << prompt << temp_arg_string << " (yes/no)" << std::endl; prompt_user = true; } else{ // Unrecognized command. std::cout << prompt << "Error! Unrecognized system command " << temp_cmd_string << ".\n"; } return ""; // Done processing the command. Don't need to send it to the caller. } // Print the command if it was read from a script. This is done so that the user // will know what is happening in the script file. It will also ignore system // commands in the file. if(from_script){ std::cout << prompt << output << "\n"; flush(); _scrollPosition = 0; clear_(); tabCount = 0; } return output; }
// Force text to the input screen void Terminal::in_print_(const char* input_){ cursX += cstrlen(input_); waddstr(input_window, input_); update_cursor_(); refresh_(); }
std::string Terminal::GetCommand(){ std::string output = ""; time_t commandRequestTime; time_t currentTime; time(&commandRequestTime); //Update status message if (status_window) { werase(status_window); print(status_window,statusStr.at(0).c_str()); } while(true){ if(SIGNAL_SEGFAULT){ // segmentation fault (SIGSEGV) Close(); return "_SIGSEGV_"; } if(SIGNAL_INTERRUPT){ // ctrl-c (SIGINT) SIGNAL_INTERRUPT = false; output = "CTRL_C"; text_length = 0; break; } else if(SIGNAL_TERMSTOP){ // ctrl-z (SIGTSTP) SIGNAL_TERMSTOP = false; output = "CTRL_Z"; text_length = 0; break; } flush(); // If there is anything in the stream, dump it to the screen //Time out if there is no command within the set interval (default 0.5 s). if (commandTimeout_ > 0) { time(¤tTime); //If the timeout has passed we simply return the empty output string. if (currentTime > commandRequestTime + commandTimeout_) { break; } } int keypress = wgetch(input_window); // Check for internal commands if(keypress == ERR){ } // No key was pressed in the interval else if(keypress == 10){ // Enter key (10) std::string temp_cmd = cmd.Get(); //Reset the position in the history. commands.Reset(); if(temp_cmd != "" && temp_cmd != commands.PeekPrev()){ // Only save this command if it is different than the previous command commands.Push(temp_cmd); } output = temp_cmd; std::cout << prompt << output << "\n"; flush(); text_length = 0; _scrollPosition = 0; clear_(); tabCount = 0; break; } else if(keypress == '\t' && enableTabComplete) { tabCount++; output = cmd.Get().substr(0,cursX - offset) + "\t"; break; } else if(keypress == 4){ // ctrl-d (EOT) output = "CTRL_D"; text_length = 0; clear_(); tabCount = 0; break; } else if(keypress == 9){ } // Tab key (9) else if(keypress == KEY_UP){ // 259 if(commands.GetIndex() == 0){ commands.Capture(cmd.Get()); } std::string temp_cmd = commands.GetPrev(); if(temp_cmd != "NULL"){ clear_(); cmd.Set(temp_cmd); in_print_(cmd.Get().c_str()); text_length = cmd.GetSize(); } } else if(keypress == KEY_DOWN){ // 258 std::string temp_cmd = commands.GetNext(); if(temp_cmd != "NULL"){ clear_(); cmd.Set(temp_cmd); in_print_(cmd.Get().c_str()); text_length = cmd.GetSize(); } } else if(keypress == KEY_LEFT){ cursX--; } // 260 else if(keypress == KEY_RIGHT){ cursX++; } // 261 else if(keypress == KEY_PPAGE){ //Page up key scroll_(-(_winSizeY-2)); } else if(keypress == KEY_NPAGE){ //Page down key scroll_(_winSizeY-2); } else if(keypress == KEY_BACKSPACE){ // 263 wmove(input_window, 0, --cursX); wdelch(input_window); cmd.Pop(cursX - offset); text_length = cmd.GetSize(); } else if(keypress == KEY_DC){ // Delete character (330) //Remove character from terminal wdelch(input_window); //Remove character from cmd string cmd.Pop(cursX - offset); text_length = cmd.GetSize(); } else if(keypress == KEY_IC){ cmd.ToggleInsertMode(); } // Insert key (331) else if(keypress == KEY_HOME){ cursX = offset; } else if(keypress == KEY_END){ cursX = text_length + offset; } else if(keypress == KEY_MOUSE) { //Handle mouse events MEVENT mouseEvent; //Get information about mouse event. getmouse(&mouseEvent); switch (mouseEvent.bstate) { //Scroll up case BUTTON4_PRESSED: scroll_(-3); break; //Scroll down. (Yes the name is strange.) case REPORT_MOUSE_POSITION: scroll_(3); break; } } else if(keypress == KEY_RESIZE) { //Do nothing with the resize key } else{ in_char_((char)keypress); cmd.Put((char)keypress, cursX - offset - 1); text_length = cmd.GetSize(); } // Check for cursor too far to the left if(cursX < offset){ cursX = offset; } // Check for cursor too far to the right if(cursX > (text_length + offset)){ cursX = text_length + offset; } //Update status message if (status_window) { werase(status_window); print(status_window,statusStr.at(0).c_str()); } if (keypress != ERR) tabCount = 0; update_cursor_(); refresh_(); } return output; }