- Timestamp:
- Aug 31, 2010, 11:13:46 AM (15 years ago)
- File:
-
- 1 copied
Legend:
- Unmodified
- Added
- Removed
-
code/trunk/src/libraries/core/command/IOConsoleWindows.cc
r7265 r7287 21 21 * 22 22 * Author: 23 * Oliver Scheuss24 23 * Reto Grieder 25 24 * Co-authors: … … 35 34 #include "util/Clock.h" 36 35 #include "util/Math.h" 37 #include "Game.h" 38 #include "input/InputBuffer.h" 39 40 // ########################## 41 // ### Mutual methods ### 42 // ########################## 36 #include "core/Game.h" 37 #include "core/input/InputBuffer.h" 38 43 39 namespace orxonox 44 40 { 45 41 IOConsole* IOConsole::singletonPtr_s = NULL; 46 42 47 // ###############################48 // ### ShellListener methods ###49 // ###############################50 51 //! Called if all output-lines have to be reprinted52 void IOConsole::linesChanged()53 {54 // Method only gets called upon start to draw all the lines55 // or when scrolling. But scrolling is disabled and the output56 // is already in std::cout when we start the IOConsole57 }58 59 //! Called if a command is about to be executed60 void IOConsole::executed()61 {62 this->shell_->addOutput(this->promptString_ + this->shell_->getInput() + '\n', Shell::Command);63 }64 65 //! Called if the console gets closed66 void IOConsole::exit()67 {68 // Exit is not an option, just do nothing (Shell doesn't really exit too)69 }70 }71 72 #ifdef ORXONOX_PLATFORM_UNIX73 // ###############################74 // ### Unix Implementation ###75 // ###############################76 77 #include <termios.h>78 #include <sys/ioctl.h>79 80 namespace orxonox81 {82 namespace EscapeMode83 {84 enum Value85 {86 None,87 First,88 Second89 };90 }91 92 IOConsole::IOConsole()93 : shell_(new Shell("IOConsole", false))94 , buffer_(shell_->getInputBuffer())95 , cout_(std::cout.rdbuf())96 , promptString_("orxonox # ")97 , bStatusPrinted_(false)98 , originalTerminalSettings_(0)99 {100 this->setTerminalMode();101 this->shell_->registerListener(this);102 103 // Manually set the widths of the individual status lines104 this->statusLineWidths_.push_back(29);105 this->statusLineMaxWidth_ = 29;106 107 this->getTerminalSize();108 this->lastTerminalWidth_ = this->terminalWidth_;109 this->lastTerminalHeight_ = this->terminalHeight_;110 111 // Disable standard std::cout logging112 OutputHandler::getInstance().disableCout();113 // Redirect std::cout to an ostringstream114 // (Other part is in the initialiser list)115 std::cout.rdbuf(this->origCout_.rdbuf());116 117 // Make sure we make way for the status lines118 this->preUpdate(Game::getInstance().getGameClock());119 }120 121 IOConsole::~IOConsole()122 {123 // Process output written to std::cout in the meantime124 std::cout.flush();125 if (!this->origCout_.str().empty())126 this->shell_->addOutput(this->origCout_.str(), Shell::None);127 // Erase input and status lines128 this->cout_ << "\033[1G\033[J";129 // Move cursor to the bottom130 this->cout_ << "\033[" << this->statusLineWidths_.size() << 'B';131 // Scroll terminal to compensate for erased lines132 this->cout_ << "\033[" << this->statusLineWidths_.size() << 'T';133 134 resetTerminalMode();135 this->shell_->destroy();136 137 // Restore this->cout_ redirection138 std::cout.rdbuf(this->cout_.rdbuf());139 // Enable standard std::cout logging again140 OutputHandler::getInstance().enableCout();141 }142 143 void IOConsole::preUpdate(const Clock& time)144 {145 unsigned char c;146 std::string escapeSequence;147 EscapeMode::Value escapeMode = EscapeMode::None;148 while (std::cin.good())149 {150 c = std::cin.get();151 if (!std::cin.good())152 break;153 154 if (escapeMode == EscapeMode::First && (c == '[' || c=='O') )155 escapeMode = EscapeMode::Second;156 // Get Alt+Tab combination when switching applications157 else if (escapeMode == EscapeMode::First && c == '\t')158 {159 this->buffer_->buttonPressed(KeyEvent(KeyCode::Tab, '\t', KeyboardModifier::Alt));160 escapeMode = EscapeMode::None;161 }162 else if (escapeMode == EscapeMode::Second)163 {164 escapeSequence += c;165 escapeMode = EscapeMode::None;166 if (escapeSequence == "A")167 this->buffer_->buttonPressed(KeyEvent(KeyCode::Up, 0, 0));168 else if (escapeSequence == "B")169 this->buffer_->buttonPressed(KeyEvent(KeyCode::Down, 0, 0));170 else if (escapeSequence == "C")171 this->buffer_->buttonPressed(KeyEvent(KeyCode::Right, 0, 0));172 else if (escapeSequence == "D")173 this->buffer_->buttonPressed(KeyEvent(KeyCode::Left, 0, 0));174 else if (escapeSequence == "1~" || escapeSequence == "H")175 this->buffer_->buttonPressed(KeyEvent(KeyCode::Home, 0, 0));176 else if (escapeSequence == "2~")177 this->buffer_->buttonPressed(KeyEvent(KeyCode::Insert, 0, 0));178 else if (escapeSequence == "3~")179 this->buffer_->buttonPressed(KeyEvent(KeyCode::Delete, 0, 0));180 else if (escapeSequence == "4~" || escapeSequence == "F")181 this->buffer_->buttonPressed(KeyEvent(KeyCode::End, 0, 0));182 else if (escapeSequence == "5~")183 this->buffer_->buttonPressed(KeyEvent(KeyCode::PageUp, 0, 0));184 else if (escapeSequence == "6~")185 this->buffer_->buttonPressed(KeyEvent(KeyCode::PageDown, 0, 0));186 else187 // Waiting for sequence to complete188 // If the user presses ESC and then '[' or 'O' while the loop is not189 // running (for instance while loading), the whole sequence gets dropped190 escapeMode = EscapeMode::Second;191 }192 else // not in an escape sequence OR user might have pressed just ESC193 {194 if (escapeMode == EscapeMode::First)195 {196 this->buffer_->buttonPressed(KeyEvent(KeyCode::Escape, c, 0));197 escapeMode = EscapeMode::None;198 }199 if (c == '\033')200 {201 escapeMode = EscapeMode::First;202 escapeSequence.clear();203 }204 else205 {206 KeyCode::ByEnum code;207 switch (c)208 {209 case '\n' : case '\r': code = KeyCode::Return; break;210 case '\177': case '\b': code = KeyCode::Back; break;211 case '\t' : code = KeyCode::Tab; break;212 default:213 // We don't encode the key code (would be a very large switch)214 // because the InputBuffer will only insert the text anyway215 // Replacement character is simply KeyCode::A216 code = KeyCode::A;217 }218 this->buffer_->buttonPressed(KeyEvent(code, c, 0));219 }220 }221 }222 // Reset error flags in std::cin223 std::cin.clear();224 225 // If there is still an escape key pending (escape key ONLY), then226 // it sure isn't an escape sequence anymore227 if (escapeMode == EscapeMode::First)228 this->buffer_->buttonPressed(KeyEvent(KeyCode::Escape, '\033', 0));229 230 // Determine terminal width and height231 this->lastTerminalWidth_ = this->terminalWidth_;232 this->lastTerminalHeight_ = this->terminalHeight_;233 this->getTerminalSize();234 235 int heightDiff = this->terminalHeight_ - this->lastTerminalHeight_;236 if (this->bStatusPrinted_ && heightDiff < 0)237 {238 // Terminal width has shrunk. The cursor will still be on the input line,239 // but that line might very well be the last240 int newLines = std::min((int)this->statusLineWidths_.size(), -heightDiff);241 // Scroll terminal to create new lines242 this->cout_ << "\033[" << newLines << 'S';243 }244 245 if (!this->bStatusPrinted_ && this->willPrintStatusLines())246 {247 // Scroll console to make way for status lines248 this->cout_ << "\033[" << this->statusLineWidths_.size() << 'S';249 this->bStatusPrinted_ = true;250 }251 252 // We always assume that the cursor is on the input line.253 // But we cannot always be sure about that, esp. if we scroll the console254 this->cout_ << "\033[" << this->statusLineWidths_.size() << 'B';255 this->cout_ << "\033[" << this->statusLineWidths_.size() << 'A';256 257 // Erase status and input lines258 this->cout_ << "\033[1G\033[J";259 this->printInputLine();260 this->printStatusLines();261 this->cout_.flush();262 263 // Process output written to std::cout264 std::cout.flush();265 if (!this->origCout_.str().empty())266 {267 this->shell_->addOutput(this->origCout_.str(), Shell::None);268 this->origCout_.str("");269 }270 }271 272 void IOConsole::printOutputLine(const std::string& text, Shell::LineType type)273 {274 /*275 // Colour line276 switch (type)277 {278 case Shell::None: this->cout_ << "\033[37m"; break;279 case Shell::Error: this->cout_ << "\033[91m"; break;280 case Shell::Warning: this->cout_ << "\033[31m"; break;281 case Shell::Info: this->cout_ << "\033[34m"; break;282 case Shell::Debug: this->cout_ << "\033[36m"; break;283 case Shell::Verbose: this->cout_ << "\033[35m"; break;284 case Shell::Ultra: this->cout_ << "\033[37m"; break;285 default: break;286 }287 */288 289 // Print output line290 this->cout_ << text;291 292 // Reset colour to white293 // this->cout_ << "\033[37m";294 }295 296 void IOConsole::printInputLine()297 {298 // Set cursor to the beginning of the line and erase the line299 this->cout_ << "\033[1G\033[K";300 // Indicate a command prompt301 this->cout_ << this->promptString_;302 // Save cursor position303 this->cout_ << "\033[s";304 // Print command line buffer305 this->cout_ << this->shell_->getInput();306 // Restore cursor position and move it to the right307 this->cout_ << "\033[u";308 if (this->buffer_->getCursorPosition() > 0)309 this->cout_ << "\033[" << this->buffer_->getCursorPosition() << 'C';310 }311 312 void IOConsole::printStatusLines()313 {314 if (this->willPrintStatusLines())315 {316 // Save cursor position317 this->cout_ << "\033[s";318 // Move cursor down (don't create a new line here because the buffer might flush then!)319 this->cout_ << "\033[1B\033[1G";320 this->cout_ << std::fixed << std::setprecision(2) << std::setw(5) << Game::getInstance().getAvgFPS() << " fps, ";321 this->cout_ << std::setprecision(2) << std::setw(5) << Game::getInstance().getAvgTickTime() << " ms tick time";322 // Restore cursor position323 this->cout_ << "\033[u";324 this->bStatusPrinted_ = true;325 }326 else327 this->bStatusPrinted_ = false;328 }329 330 void IOConsole::setTerminalMode()331 {332 termios new_settings;333 this->originalTerminalSettings_ = new termios();334 335 tcgetattr(0, this->originalTerminalSettings_);336 new_settings = *this->originalTerminalSettings_;337 new_settings.c_lflag &= ~(ICANON | ECHO);338 //new_settings.c_lflag |= (ISIG | IEXTEN);339 new_settings.c_cc[VTIME] = 0;340 new_settings.c_cc[VMIN] = 0;341 tcsetattr(0, TCSANOW, &new_settings);342 atexit(&IOConsole::resetTerminalMode);343 }344 345 /*static*/ void IOConsole::resetTerminalMode()346 {347 if (IOConsole::singletonPtr_s && IOConsole::singletonPtr_s->originalTerminalSettings_)348 {349 tcsetattr(0, TCSANOW, IOConsole::singletonPtr_s->originalTerminalSettings_);350 delete IOConsole::singletonPtr_s->originalTerminalSettings_;351 IOConsole::singletonPtr_s->originalTerminalSettings_ = 0;352 }353 }354 355 void IOConsole::getTerminalSize()356 {357 #ifdef TIOCGSIZE358 struct ttysize win;359 if (!ioctl(STDIN_FILENO, TIOCGSIZE, &win))360 {361 this->terminalWidth_ = win.ts_cols;362 this->terminalHeight_ = win.ts_lines;363 return;364 }365 #elif defined TIOCGWINSZ366 struct winsize win;367 if (!ioctl(STDIN_FILENO, TIOCGWINSZ, &win))368 {369 this->terminalWidth_ = win.ws_col;370 this->terminalHeight_ = win.ws_row;371 return;372 }373 #else374 const char* s = getenv("COLUMNS");375 this->terminalWidth_ = s ? strtol(s, NULL, 10) : 80;376 s = getenv("LINES");377 this->terminalHeight_ = s ? strtol(s, NULL, 10) : 24;378 return;379 #endif380 this->terminalWidth_ = 80;381 this->terminalHeight_ = 24;382 }383 384 inline bool IOConsole::willPrintStatusLines()385 {386 return !this->statusLineWidths_.empty()387 && this->terminalWidth_ >= this->statusLineMaxWidth_388 && this->terminalHeight_ >= this->minOutputLines_ + (int)this->statusLineWidths_.size();389 }390 391 // ###############################392 // ### ShellListener methods ###393 // ###############################394 395 //! Called if only the last output-line has changed396 void IOConsole::onlyLastLineChanged()397 {398 // Save cursor position and move it to the beginning of the first output line399 this->cout_ << "\033[s\033[1A\033[1G";400 // Erase the line401 this->cout_ << "\033[K";402 // Reprint the last output line403 this->printOutputLine(this->shell_->getNewestLineIterator()->first, this->shell_->getNewestLineIterator()->second);404 // Restore cursor405 this->cout_ << "\033[u";406 this->cout_.flush();407 }408 409 //! Called if a new output-line was added410 void IOConsole::lineAdded()411 {412 int newLines = this->shell_->getNewestLineIterator()->first.size() / this->terminalWidth_ + 1;413 // Create new lines by scrolling the screen414 this->cout_ << "\033[" << newLines << 'S';415 // Move cursor to the beginning of the new (last) output line416 this->cout_ << "\033[" << newLines << "A\033[1G";417 // Erase screen from here418 this->cout_ << "\033[J";419 // Print the new output lines420 for (int i = 0; i < newLines; ++i)421 {422 Shell::LineList::const_iterator it = this->shell_->getNewestLineIterator();423 this->printOutputLine(it->first.substr(i*this->terminalWidth_, this->terminalWidth_), it->second);424 }425 // Move cursor down426 this->cout_ << "\033[1B\033[1G";427 // Print status and input lines428 this->printInputLine();429 this->printStatusLines();430 this->cout_.flush();431 }432 433 //! Called if the text in the input-line has changed434 void IOConsole::inputChanged()435 {436 this->printInputLine();437 this->cout_.flush();438 }439 440 //! Called if the position of the cursor in the input-line has changed441 void IOConsole::cursorChanged()442 {443 this->printInputLine();444 this->cout_.flush();445 }446 }447 448 #elif defined(ORXONOX_PLATFORM_WINDOWS)449 // ##################################450 // ### Windows Implementation ###451 // ##################################452 453 #include <windows.h>454 455 namespace orxonox456 {457 43 //! Redirects std::cout, creates the corresponding Shell and changes the terminal mode 458 44 IOConsole::IOConsole() … … 720 306 // ############################### 721 307 308 //! Called if all output-lines have to be reprinted 309 void IOConsole::linesChanged() 310 { 311 // Method only gets called upon start to draw all the lines 312 // or when scrolling. But scrolling is disabled and the output 313 // is already in std::cout when we start the IOConsole 314 } 315 316 //! Called if a command is about to be executed 317 void IOConsole::executed() 318 { 319 this->shell_->addOutput(this->promptString_ + this->shell_->getInput() + '\n', Shell::Command); 320 } 321 322 //! Called if the console gets closed 323 void IOConsole::exit() 324 { 325 // Exit is not an option, just do nothing (Shell doesn't really exit too) 326 } 327 722 328 //! Called if the text in the input line has changed 723 329 void IOConsole::inputChanged() … … 796 402 } 797 403 } 798 799 #endif /* ORXONOX_PLATFORM_UNIX */
Note: See TracChangeset
for help on using the changeset viewer.