/* server/console.cc This file is part of the Osirion project and is distributed under the terms and conditions of the GNU General Public License version 2 */ #include #include #include #include #include #include #include "auxiliary/functions.h" #include "core/application.h" #include "core/core.h" #include "core/commandbuffer.h" #include "dedicated/console.h" #include "sys/consoleinterface.h" #ifdef HAVE_CURSES #include #endif namespace dedicated { bool console_initialized = false; bool console_updated = false; const size_t MAXHISTOLINES = 512; float prev_time = 0; Console server_console; #ifdef HAVE_CURSES WINDOW *stdwin; #endif Console *console() { return (&server_console); } void Console::init() { #ifdef HAVE_CURSES stdwin = initscr(); // initialize the ncurses window cbreak(); // disable input line buffering noecho(); // don't show typed characters keypad(stdwin, TRUE); // enable special keys nodelay(stdwin, TRUE); // non-blocking input curs_set(1); // enable cursor if (has_colors() == TRUE) { start_color(); // this is ncurses-specific use_default_colors(); // COLOR_PAIR(0) is terminal default init_pair(1, COLOR_RED, -1); init_pair(2, COLOR_GREEN, -1); init_pair(3, COLOR_YELLOW, -1); init_pair(4, COLOR_BLUE, -1); init_pair(5, COLOR_CYAN, -1); init_pair(6, COLOR_MAGENTA, -1); init_pair(7, COLOR_WHITE, -1); init_pair(8, -1, -1); } #endif // HAVE_CURSES con_print << "^BInitializing console..." << std::endl; #ifdef HAVE_CURSES server_console.history.clear(); server_console.history.push_back(""); server_console.history_pos = server_console.history.rbegin(); server_console.input_pos = 0; server_console.console_scroll = 0; server_console.draw(); #endif // HAVE_CURSES console_initialized = true; console_updated = true; } void Console::shutdown() { con_print << "^BShutting down console..." << std::endl; #ifdef HAVE_CURSES server_console.draw(); endwin(); console_initialized = false; server_console.dump(); #endif } Console::Console() { } Console::~Console() { } #ifdef HAVE_CURSES void Console::dump() { // dump console content for (Queue::iterator it = log().begin(); it != log().end(); it++) { sys::ConsoleInterface::print((*it)); } } void Console::resize() { if (!console_initialized) return; endwin(); refresh(); draw(); } void Console::print(const std::string & text) { if (console_initialized && !rcon()) { console_updated = true; draw(); } } void Console::set_color(const char *color_code) { if (!has_colors()) return; int color = 0; bool bold = false; if (aux::is_base_color_code(color_code)) { // base colors // Default=0, Red=1, Green=2, Yellow=3, Blue=4, Cyan=5, Magenta=6, White=7 color = *(color_code + 1) - '0'; if (color == 3 || color == 7) bold = true; else bold = false; } else if (aux::is_core_color_code(color_code)) { switch (*(color_code + 1)) { case 'N': // normal color case 'D': // debug color color = 0; break; case 'B': // bold color color = 0; bold = true; break; case 'W': // warning color color = 3; bold = true; break; case 'R': // error color color = 1; bold = true; break; case 'F': // fancy color color = 2; bold = true; break; } } color_set(color, NULL); if (bold) attron(A_BOLD); else attroff(A_BOLD); } void Console::draw_background() { color_set(0, NULL); attroff(A_BOLD); bkgdset(' '); clear(); // draw version string color_set(2, NULL); std::string versionstr("Project::OSiRiON "); versionstr.append(core::version()); int y = console_width - versionstr.size(); if (y < 0) y = 0; mvaddnstr(0, y, versionstr.c_str(), console_width); } void Console::draw_status() { if (core::game()) { attroff(A_BOLD); color_set(2, NULL); int minutes = (int) floorf(core::game()->time() / 60.0f); int seconds = (int) floorf(core::game()->time() - (float) minutes * 60.0f); std::stringstream status; status << "time " << std::setfill(' ') << std::setw(3) << minutes << ":" << std::setfill('0') << std::setw(2) << seconds; mvaddnstr(0, 1, status.str().c_str(), status.str().size()); } } void Console::draw_text() { if ((console_width < 4) || (console_height < 3)) return; Queue lines; int bottom = (int) log().size() - console_scroll; int current_line = 0; // parse console text, wrap long lines for (Queue::iterator it = log().begin(); it != log().end() && current_line < bottom; it++) { if (current_line >= bottom - console_height - 1) { std::string linedata(*it); linedata += '\n'; std::string word; int word_length = 0; std::string line; int line_length = 0; const char *c = linedata.c_str(); char pen = 'N'; char wordpen = 'N'; while (*c) { // color code if (aux::is_color_code(c)) { c++; pen = *c; word += '^'; word += pen; } // new word, wrap if necessary else if ((*c == '\n') || (*c == ' ')) { if (line_length + word_length > console_width) { if (line.size()) { lines.push_back(line); line.clear(); line += '^'; line += wordpen; line_length = 0; } } line.append(word); line_length += word_length; word.clear(); word_length = 0; wordpen = pen; // new line if (*c == '\n') { lines.push_back(line); line.clear(); line_length = 0; // new word } else if (*c == ' ') { line += ' '; line_length++; } } // new character else { word += *c; word_length++; if (word_length == console_width) { if (line.size()) { lines.push_back(line); line.clear(); line += '^'; line += wordpen; line_length = 0; } line.append(word); line_length = word_length; word.clear(); word_length = 0; wordpen = pen; } } c++; } } current_line++; } int y = console_height - 1; color_set(0, NULL); attroff(A_BOLD); for (Queue::reverse_iterator rit = lines.rbegin(); (y > 0) && (rit != lines.rend()); ++rit) { const char *c = (*rit).c_str(); int x = 0; while (*c) { if (aux::is_color_code(c)) { set_color(c); c++; } else { mvaddnstr(y, x, c, 1); x++; } c++; } y--; } } void Console::draw_input() { color_set(0, NULL); attron(A_BOLD); // draw input text mvaddstr(console_height, 0, ">"); mvaddstr(console_height, 1, (*history_pos).c_str()); // fill the remainder with spaces for (int i = 1 + (*history_pos).size(); i < console_width; i++) addch(' '); } void Console::draw() { if (!console_initialized) return; #ifdef _WIN32 console_width = stdwin->_maxx - 1; console_height = stdwin->_maxy - 1; #else console_width = stdwin->_maxx; console_height = stdwin->_maxy; #endif draw_background(); draw_status(); draw_text(); draw_input(); move(console_height, 1 + input_pos); wrefresh(stdwin); console_updated = false; } void Console::frame() { const size_t scroll_offset = 3; History::reverse_iterator upit; if (!console_initialized) return; bool input_updated = false; int key = wgetch(stdwin); while (key != ERR) { if (key == KEY_BACKSPACE || key == 8 || key == 127) { if ((*history_pos).size() && input_pos) { (*history_pos).erase(input_pos - 1, 1); input_pos--; input_updated = true; } break; } else if (key == KEY_STAB || key == 9) { core::CommandBuffer::complete((*history_pos), input_pos); input_updated = true; break; } else if (key == KEY_LEFT) { if (input_pos > 0) { input_pos--; } input_updated = true; break; } else if (key == KEY_RIGHT) { if (input_pos < (*history_pos).size()) { input_pos++; } input_updated = true; break; } else if (key == KEY_HOME) { input_pos = 0; input_updated = true; break; } else if (key == KEY_END) { input_pos = (*history_pos).size(); input_updated = true; break; } else if (key == KEY_UP) { upit = history_pos; ++upit; if (upit != history.rend()) { history_pos = upit; input_pos = (*history_pos).size(); input_updated = true; } break; } else if (key == KEY_DOWN) { if (history_pos != history.rbegin()) { --history_pos; input_pos = (*history_pos).size(); input_updated = true; } break; } else if (key == KEY_ENTER || key == '\n') { if ((*history_pos).size()) { // store input into history while (history.size() >= MAXHISTOLINES) { history.pop_front(); } core::cmd() << (*history_pos) << std::endl; con_print << "^B>" << (*history_pos) << std::endl; (*history.rbegin()) = (*history_pos); history.push_back(""); history_pos = history.rbegin(); input_pos = 0; console_updated = true; } break; } else if (key == KEY_PPAGE) { console_scroll += scroll_offset; if (console_scroll > log().size()) console_scroll = log().size(); console_updated = true; break; } else if (key == KEY_NPAGE) { if (console_scroll > scroll_offset) console_scroll -= scroll_offset; else console_scroll = 0; console_updated = true; break; } else if ((key >= 32) && (key < 127) && ((*history_pos).size() < MAXCMDSIZE)) { if (input_pos == (*history_pos).size()) { (*history_pos) += (char)key; } else { (*history_pos).insert(input_pos, 1, (char)key); } input_pos++; input_updated = true; } key = wgetch(stdwin); } if (console_updated) { draw(); } else { if (input_updated) { draw_input(); } if (roundf(core::application()->time()) != prev_time) { draw_status(); prev_time = roundf(core::application()->time()); input_updated = true; } if (input_updated) { // move the cursor to input position move(console_height, 1 + input_pos); wrefresh(stdwin); } } } #endif // HAVE_CURSES }