/* 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 "filesystem/filesystem.h" #include "core/core.h" #include "render/render.h" #include "client/console.h" #include "client/video.h" #include "client/keyboard.h" #include #include #include namespace client { namespace console { //--- private definition ------------------------------------------ /// private client console implementation class Console : public sys::ConsoleInterface { public: /// stream to send normal messages too virtual std::ostream & messagestream(); /// stream to send warning messages too virtual std::ostream & warningstream(); /// stream to send error messages too virtual std::ostream & errorstream(); /// stream to send debug messages too virtual std::ostream & debugstream(); /// flush buffered messages virtual void flush(); /// console text buffer std::stringstream buffer; }; // private client console object Console console; // console text data std::deque text; // input history std::deque history; std::deque::reverse_iterator history_pos; size_t input_pos = 0; // console visibility bool console_visible = false; size_t console_scroll = 0; // notifications size_t notify_pos = 0; std::string notify_text[MAXNOTIFYLINES]; float notify_time[MAXNOTIFYLINES]; //--- engine functions -------------------------------------------- void func_con_toggle(std::string const &args) { std::istringstream argstream(args); int i; if (argstream >> i) { if (i) console_visible = true; else console_visible = false; } else console_visible = !console_visible; } //--- public ------------------------------------------------------ void init() { con_print << "Initializing console..." << std::endl; console_visible = false; // add engine functions core::Func::add("con_toggle", (core::FuncPtr) func_con_toggle); text.clear(); console_scroll = 0; history.clear(); history.push_back(""); history_pos = history.rbegin(); input_pos = 0; load_history(); } void shutdown() { con_print << "Shutting down console..." << std::endl; save_history(); // remove engine functions //core::Func::remove("con_toggle"); text.clear(); console_scroll = 0; history.clear(); input_pos = 0; } void draw() { using namespace render; if(!console_visible) return; float con_height = 0.70f; // draw version below the bottom of the console gl::color(0.0f, 1.0f, 0.0f, 0.5f); std::string version(core::name()); version += ' '; version.append(core::version()); draw_text(video::width-CHARWIDTH*(version.size()+1), video::height*con_height-CHARHEIGHT-4, version); gl::disable(GL_TEXTURE_2D); // draw the transparent console background gl::color(1.0f, 1.0f, 1.0f, 0.02f); 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(); // draw the console text if (console_scroll > text.size()) console_scroll = text.size(); gl::enable(GL_TEXTURE_2D); std::deque::reverse_iterator rit = text.rbegin(); float bottom = video::height*con_height-2*CHARHEIGHT-8; float y = bottom+console_scroll*CHARHEIGHT; while (y > 0 && rit < text.rend()) { if (y <= bottom) { std::string line(*rit); if (line[0] == '?') gl::color(0.7f,0.7f,0.7f, 1.0f); else if (line[0] == '*') gl::color(1.0f,1.0f,0.0f, 1.0f); else if (line[0] == '!') gl::color(1.0f,0.0f,0.0f, 1.0f); else gl::color(1.0f,1.0f,1.0f, 1.0f); line.erase(0,2); draw_text(CHARWIDTH, y, line); } y -= CHARHEIGHT; ++rit; } // draw the console input gl::color(0.0f, 1.0f, 0.0f, 1.0f); draw_text(CHARWIDTH, video::height*con_height - CHARHEIGHT - 4, (*history_pos)); // draw cursor if ((core::application()->time() - ::floorf(core::application()->time())) < 0.5f) { std::string cursor("_"); draw_text(CHARWIDTH*(input_pos+1), video::height*con_height - CHARHEIGHT - 4 , cursor); } } void flush() { char line[MAXCMDSIZE]; while(console.buffer.getline(line, MAXCMDSIZE-1)) { while (text.size() >= MAXCONLINES) { text.pop_front(); } text.push_back(std::string(line)); // save notification notify_text[notify_pos] = line; notify_time[notify_pos] = core::application()->time(); notify_pos = (notify_pos+1) % MAXNOTIFYLINES; // print to stdout std::cout << line << std::endl; } console.buffer.clear(); } void 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); } setkeyboardmode(console::visible()); } void 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; (*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 > text.size()) console_scroll = 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; } } bool visible() { return console_visible; } void 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 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; } //--- private ----------------------------------------------------- void Console::flush() { console::flush(); } std::ostream & Console::messagestream() { return (buffer << ". "); } std::ostream & Console::warningstream() { return (buffer << "* "); } std::ostream & Console::errorstream() { return (buffer << "! "); } std::ostream & Console::debugstream() { return (buffer << "? "); } } // namespace console } // namespace client