/* client/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 "client/console.h" #include "auxiliary/functions.h" #include "core/core.h" #include "filesystem/filesystem.h" #include "render/render.h" #include "render/textures.h" #include "client/video.h" #include "client/keyboard.h" #include #include #include namespace client { Console client_console; Console *console() { return &client_console; } //--- engine functions -------------------------------------------- void func_ui_console(std::string const &args) { console()->toggle(); } //--- public ------------------------------------------------------ void Console::init() { con_print << "^BInitializing console..." << std::endl; core::Func *func = core::Func::add("ui_console", (core::FuncPtr) func_ui_console); func->set_info("toggle console on or off"); console()->load_history(); } void Console::shutdown() { con_print << "^BShutting down console..." << std::endl; console()->save_history(); // remove engine functions core::Func::remove("ui_console"); } //--- Console ----------------------------------------------------- Console::Console() { clear(); } Console::~Console() { history.clear(); } void Console::clear() { console_visible = false; console_scroll = 0; history.clear(); history.push_back(""); history_pos = history.rbegin(); input_pos = 0; clear_notify(); } void Console::draw() { if (visible()) draw_console(); else draw_notify(); } void Console::toggle() { console_visible = !console_visible; if (console_visible) { console_scroll = 0; input_pos = 0; history_pos = history.rbegin(); (*history_pos).clear(); SDL_WM_GrabInput(SDL_GRAB_OFF); SDL_ShowCursor(SDL_ENABLE); } else { SDL_WM_GrabInput(SDL_GRAB_ON); SDL_ShowCursor(SDL_DISABLE); //clear_notify(); } setkeyboardmode(visible()); } void Console::keypressed(int key) { std::deque::reverse_iterator upit; switch( key ) { case SDLK_TAB: core::CommandBuffer::complete( (*history_pos), input_pos); break; case SDLK_RETURN: 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; } break; case SDLK_UP: upit = history_pos; ++upit; if (upit != history.rend()) { history_pos = upit; input_pos = (*history_pos).size(); } break; case SDLK_DOWN: if (history_pos != history.rbegin()) { --history_pos; input_pos = (*history_pos).size(); } break; case SDLK_HOME: input_pos = 0; break; case SDLK_END: input_pos = (*history_pos).size(); break; case SDLK_LEFT: if (input_pos > 0) input_pos--; break; case SDLK_RIGHT: if (input_pos < (*history_pos).size()) input_pos++; break; case SDLK_BACKSPACE: if ((*history_pos).size() && input_pos) { (*history_pos).erase(input_pos-1, 1); input_pos--; } break; case SDLK_PAGEUP: console_scroll +=5; if (console_scroll > consoleinterface_text.size()) console_scroll = consoleinterface_text.size(); break; case SDLK_PAGEDOWN: if (console_scroll > 5) console_scroll -=5; else console_scroll = 0; break; default: if ((key >= 32 ) && (key <175)) { if (input_pos == (*history_pos).size()) (*history_pos) += (char)key; else (*history_pos).insert(input_pos, 1, (char)key); input_pos++; } break; } } void Console::save_history() { if (history.size() <= 1) return; std::string filename(filesystem::writedir); filename.append("history.txt"); std::ofstream ofs(filename.c_str()); if (!ofs.is_open()) { con_warn << "Could not write " << filename << std::endl; return; } std::deque::iterator it; size_t l = 1; for (it = history.begin(); it != history.end(); it++) { if (l < history.size()) ofs << (*it) << std::endl; l++; } ofs.close(); } void Console::load_history() { std::string filename(filesystem::writedir); filename.append("history.txt"); std::ifstream ifs(filename.c_str(), std::ifstream::in); if (!ifs.is_open()) { con_warn << "Could not read " << filename << std::endl; return; } history.clear(); char line[MAXCMDSIZE]; while (ifs.getline(line, MAXCMDSIZE-1)) { history.push_back(line); } ifs.close(); history.push_back(""); history_pos = history.rbegin(); input_pos = 0; } void Console::draw_notify() { using namespace render; // draw notifications size_t width = (size_t) ((video::width-8) / Text::fontwidth()); size_t n = notify_pos % MAXNOTIFYLINES; float h = video::height/2; for (size_t l = 0; l < MAXNOTIFYLINES; l++) { if (notify_text[n].size() > 2 && notify_time[n] + 5 > core::application()->time()) { std::string linedata(notify_text[n]); linedata += '\n'; std::string word; size_t word_length = 0; std::string line; size_t line_length = 0; const char *c = linedata.c_str(); char pen = 'N'; char wordpen = 'N'; Text::setcolor('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 > width) { if (line.size()) { Text::draw(4, h, line); h += Text::fontheight(); 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' ) { Text::draw(4, h, line); h += Text::fontheight(); line.clear(); line_length = 0; // new word } else if (*c == ' ' ) { line += ' '; line_length++; } } // new character else { word += *c; word_length++; if (word_length == width) { if (line.size()) { Text::draw(4, h, line); h += Text::fontheight(); line.clear(); line += '^'; line += wordpen; line_length = 0; } line.append(word); line_length = word_length; word.clear(); word_length = 0; wordpen = pen; } } c++; } } n = (n+1) % MAXNOTIFYLINES; } } void Console::draw_console() { using namespace render; float con_height = 0.70f; gl::disable(GL_TEXTURE_2D); // draw the transparent console background gl::color(0.0f, 0.0f, 0.0f, 0.5f); gl::begin(gl::Quads); gl::vertex(0.0f, 0.0f, 0.0f); gl::vertex(video::width, 0.0f,0.0f); gl::vertex(video::width,video::height*con_height,0.0f); gl::vertex(0,video::height*con_height,0.0f); gl::end(); gl::enable(GL_TEXTURE_2D); // draw version below the bottom of the console std::string version(core::name()); version += ' '; version.append(core::version()); gl::color(0.0f, 1.0f, 0.0f, 0.5f); Text::draw(video::width-Text::fontwidth()*(version.size()+1), video::height*con_height-Text::fontheight()-4, version); // draw the console consoleinterface_text if (console_scroll > consoleinterface_text.size()) console_scroll = consoleinterface_text.size(); size_t height = (size_t) (video::height * con_height / Text::fontheight()) -1; size_t width = (size_t) ((video::width-8) / Text::fontwidth()); size_t bottom = consoleinterface_text.size() - console_scroll; size_t current_line = 0; std::deque lines; for (std::deque::iterator it = consoleinterface_text.begin(); it != consoleinterface_text.end() && current_line < bottom; it++) { if (current_line >= bottom - height) { std::string linedata(*it); linedata += '\n'; std::string word; size_t word_length = 0; std::string line; size_t 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 > 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 == 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++; } float y = video::height*con_height-2*Text::fontheight()-4; Text::setcolor('N'); for (std::deque::reverse_iterator rit = lines.rbegin(); (y >= 4) && (rit != lines.rend()); ++rit) { Text::draw(4, y, (*rit)); y -= Text::fontheight(); } // draw the console input y = video::height*con_height - Text::fontheight() - 4; Text::draw(4, y, "^B>"); std::string firstpart((*history_pos).substr(0, input_pos)); size_t draw_width = 0; const char *c = firstpart.c_str(); while (*c) { if (aux::is_color_code(c)) { c++; } else { draw_width++; } c++; } c = firstpart.c_str(); while (*c && draw_width > width - 2) { if (aux::is_color_code(c)) { c++; Text::setcolor(*c); } else { draw_width--; } c++; } if (*c) { Text::draw(4+Text::fontwidth(), y, c); } if (input_pos < (*history_pos).size()) { // FIXME limit to width if (input_pos > 1 && aux::is_color_code((*history_pos).c_str() + input_pos -1)) { Text::setcolor((*history_pos)[input_pos]); } c = (*history_pos).c_str() + input_pos; Text::draw(4+Text::fontwidth()*(draw_width+1), y, c); } // draw cursor if ((core::application()->time() - ::floorf(core::application()->time())) < 0.5f) { std::string cursor("^B_"); Text::draw(4+Text::fontwidth()*(draw_width+1), y , cursor); } } void Console::clear_notify() { for (size_t i=0; i < MAXNOTIFYLINES; i++) { notify_text[i].clear(); notify_time[i] = 0; } notify_pos = 0; } void Console::notify(std::string const & message) { // save notification notify_text[notify_pos] = message; notify_time[notify_pos] = core::application()->time(); notify_pos = (notify_pos+1) % MAXNOTIFYLINES; } } // namespace client