vrheights
diff src/console.cc @ 5:053a52f0cb64
console
author | John Tsiombikas <nuclear@member.fsf.org> |
---|---|
date | Fri, 26 Sep 2014 18:40:15 +0300 |
parents | |
children | 7f6d68d95c22 |
line diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/src/console.cc Fri Sep 26 18:40:15 2014 +0300 1.3 @@ -0,0 +1,434 @@ 1.4 +#include <stdio.h> 1.5 +#include <stdlib.h> 1.6 +#include <string.h> 1.7 +#include <stdarg.h> 1.8 +#include <assert.h> 1.9 +#include <algorithm> 1.10 +#include "opengl.h" 1.11 +#include "console.h" 1.12 + 1.13 +Console::Console() 1.14 + : prompt("> ") 1.15 +{ 1.16 + visible = false; 1.17 + nlines = 16; 1.18 + ncolumns = 80; 1.19 + pos_x = pos_y = 0; 1.20 + anchor = TOP_LEFT; 1.21 + 1.22 + inpq_front = inpq_back = 0; 1.23 + 1.24 + max_hist_lines = 64; 1.25 + max_output_lines = 128; 1.26 + 1.27 + hist_iter_valid = false; 1.28 + 1.29 + echo = true; 1.30 + font = 0; 1.31 + font_size = 0; 1.32 + 1.33 + cursor = 0; 1.34 + input_win = 0; 1.35 +} 1.36 + 1.37 +void Console::set_cursor(int x) 1.38 +{ 1.39 + if(x < 0 || x > (int)input.size()) { 1.40 + return; 1.41 + } 1.42 + cursor = x; 1.43 + 1.44 + int max_chars = ncolumns - (int)prompt.size() - 1; 1.45 + 1.46 + if(cursor < input_win) { 1.47 + input_win = cursor; 1.48 + } else if(cursor > input_win + max_chars) { 1.49 + input_win = std::max(cursor - max_chars, 0); 1.50 + } 1.51 +} 1.52 + 1.53 +int Console::get_cursor() const 1.54 +{ 1.55 + return cursor; 1.56 +} 1.57 + 1.58 +void Console::set_font(dtx_font *font, int sz) 1.59 +{ 1.60 + this->font = font; 1.61 + font_size = sz; 1.62 +} 1.63 + 1.64 +void Console::set_history_size(int hsz) 1.65 +{ 1.66 + max_hist_lines = hsz; 1.67 + 1.68 + int drop = hist.size() - hsz; 1.69 + if(drop > 0) { 1.70 + auto it = hist.begin(); 1.71 + for(int i=0; i<drop; i++) { 1.72 + ++it; 1.73 + } 1.74 + hist.erase(hist.begin(), it); 1.75 + } 1.76 +} 1.77 + 1.78 +void Console::set_output_buffer_size(int sz) 1.79 +{ 1.80 + max_output_lines = sz; 1.81 + 1.82 + int drop = output.size() - sz; 1.83 + if(drop > 0) { 1.84 + output.erase(output.begin(), output.begin() + drop); 1.85 + } 1.86 +} 1.87 + 1.88 +void Console::set_command_func(std::function<void (const char*)> func) 1.89 +{ 1.90 + cmd_handler = func; 1.91 +} 1.92 + 1.93 +void Console::set_echo(bool echo) 1.94 +{ 1.95 + this->echo = echo; 1.96 +} 1.97 + 1.98 +bool Console::get_echo() const 1.99 +{ 1.100 + return echo; 1.101 +} 1.102 + 1.103 +void Console::set_visible(bool v) 1.104 +{ 1.105 + visible = v; 1.106 +} 1.107 + 1.108 +bool Console::is_visible() const 1.109 +{ 1.110 + return visible; 1.111 +} 1.112 + 1.113 +void Console::set_size(int lines, int columns) 1.114 +{ 1.115 + nlines = lines; 1.116 + ncolumns = columns; 1.117 +} 1.118 + 1.119 +int Console::get_size_lines() const 1.120 +{ 1.121 + return nlines; 1.122 +} 1.123 + 1.124 +int Console::get_size_columns() const 1.125 +{ 1.126 + return ncolumns; 1.127 +} 1.128 + 1.129 +void Console::set_position(int x, int y, Anchor anchor) 1.130 +{ 1.131 + pos_x = x; 1.132 + pos_y = y; 1.133 + this->anchor = anchor; 1.134 +} 1.135 + 1.136 +bool Console::update() 1.137 +{ 1.138 + bool must_redraw = false; 1.139 + 1.140 + while(inpq_front != inpq_back) { 1.141 + int c = keybuf[inpq_front]; 1.142 + inpq_front = (inpq_front + 1) % INPUTQ_SIZE; 1.143 + 1.144 + switch(c) { 1.145 + case '\n': 1.146 + case '\r': 1.147 + if(echo) { 1.148 + puts(input.c_str()); 1.149 + putchar('\n'); 1.150 + } 1.151 + if(!input.empty() && cmd_handler) { 1.152 + cmd_handler(input.c_str()); 1.153 + } 1.154 + 1.155 + hist.push_back(std::move(input)); // move the input string into the history buffer 1.156 + if((int)hist.size() > max_hist_lines) { 1.157 + hist.pop_front(); 1.158 + } 1.159 + hist_iter_valid = false; 1.160 + 1.161 + set_cursor(0); 1.162 + must_redraw = true; 1.163 + break; 1.164 + 1.165 + case '\t': 1.166 + // TODO: completion 1.167 + break; 1.168 + 1.169 + case '\b': 1.170 + if(!input.empty()) { 1.171 + if(cursor == (int)input.size()) { 1.172 + input.pop_back(); 1.173 + set_cursor(get_cursor() - 1); 1.174 + must_redraw = true; 1.175 + } else if(cursor > 0) { 1.176 + input.erase(cursor - 1, 1); 1.177 + set_cursor(get_cursor() - 1); 1.178 + must_redraw = true; 1.179 + } 1.180 + } 1.181 + break; 1.182 + 1.183 + case KEY_UP: 1.184 + if(!hist.empty()) { 1.185 + if(!hist_iter_valid) { 1.186 + hist_iter = hist.rbegin(); 1.187 + hist_iter_valid = true; 1.188 + input = *hist_iter; 1.189 + } else { 1.190 + if(++hist_iter == hist.rend()) { 1.191 + --hist_iter; 1.192 + break; 1.193 + } 1.194 + input = *hist_iter; 1.195 + } 1.196 + set_cursor(input.size()); 1.197 + } 1.198 + break; 1.199 + 1.200 + case KEY_DOWN: 1.201 + if(!hist.empty()) { 1.202 + if(!hist_iter_valid) { 1.203 + hist_iter = hist.rbegin(); 1.204 + hist_iter_valid = true; 1.205 + } 1.206 + if(hist_iter != hist.rbegin()) { 1.207 + input = *--hist_iter; 1.208 + set_cursor(input.size()); 1.209 + } 1.210 + } 1.211 + break; 1.212 + 1.213 + case KEY_LEFT: 1.214 + if(cursor > 0) { 1.215 + set_cursor(get_cursor() - 1); 1.216 + must_redraw = true; 1.217 + } 1.218 + break; 1.219 + 1.220 + case KEY_RIGHT: 1.221 + if(cursor < (int)input.size()) { 1.222 + set_cursor(get_cursor() + 1); 1.223 + must_redraw = true; 1.224 + } 1.225 + break; 1.226 + 1.227 + case KEY_HOME: 1.228 + set_cursor(0); 1.229 + must_redraw = true; 1.230 + break; 1.231 + 1.232 + case KEY_END: 1.233 + set_cursor(input.size()); 1.234 + must_redraw = true; 1.235 + break; 1.236 + 1.237 + case KEY_PGUP: 1.238 + case KEY_PGDOWN: 1.239 + // TODO scroll output buffer 1.240 + break; 1.241 + 1.242 + default: 1.243 + if(c < 256 && isprint(c)) { 1.244 + if(cursor == (int)input.size()) { 1.245 + input.push_back(c); 1.246 + } else { 1.247 + input.insert(cursor, 1, c); 1.248 + } 1.249 + set_cursor(get_cursor() + 1); 1.250 + must_redraw = true; 1.251 + } 1.252 + } 1.253 + } 1.254 + return must_redraw; 1.255 +} 1.256 + 1.257 +void Console::draw() const 1.258 +{ 1.259 + if(!font || !visible) return; 1.260 + dtx_use_font(font, font_size); 1.261 + 1.262 + std::string buflines; 1.263 + 1.264 + int num_lines = std::min(nlines, (int)output.size()); 1.265 + auto it = output.cbegin() + (output.size() - num_lines); 1.266 + 1.267 + float max_width = dtx_glyph_width('Q') * ncolumns; 1.268 + 1.269 + while(it != output.cend()) { 1.270 + const std::string &line = *it++; 1.271 + buflines += line + std::string("\n"); 1.272 + 1.273 + //max_width = std::max(max_width, dtx_string_width(line.c_str())); 1.274 + } 1.275 + 1.276 + // draw the output box 1.277 + float line_height = dtx_line_height(); 1.278 + float outbox_height = nlines * line_height; 1.279 + float console_height = outbox_height + line_height * 1.5; 1.280 + 1.281 + 1.282 + int vp[4]; 1.283 + glGetIntegerv(GL_VIEWPORT, vp); 1.284 + 1.285 + int px, py; 1.286 + switch(anchor) { 1.287 + case TOP_LEFT: 1.288 + px = pos_x; 1.289 + py = pos_y; 1.290 + break; 1.291 + 1.292 + case BOTTOM_LEFT: 1.293 + px = pos_x; 1.294 + py = vp[3] - console_height - pos_y; 1.295 + break; 1.296 + 1.297 + case CENTER: 1.298 + px = (vp[2] - max_width) / 2 + pos_x; 1.299 + py = (vp[3] - console_height) / 2 + pos_y; 1.300 + break; 1.301 + } 1.302 + 1.303 + glMatrixMode(GL_PROJECTION); 1.304 + glPushMatrix(); 1.305 + glLoadIdentity(); 1.306 + glOrtho(0, vp[2], 0, vp[3], -1, 1); 1.307 + 1.308 + glMatrixMode(GL_MODELVIEW); 1.309 + glPushMatrix(); 1.310 + glLoadIdentity(); 1.311 + glTranslatef(px, vp[3] - py, 0); 1.312 + 1.313 + 1.314 + glPushAttrib(GL_ENABLE_BIT); 1.315 + glDisable(GL_DEPTH_TEST); 1.316 + glDisable(GL_LIGHTING); 1.317 + glDisable(GL_STENCIL_TEST); 1.318 + glEnable(GL_BLEND); 1.319 + glDisable(GL_TEXTURE_2D); 1.320 + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 1.321 + 1.322 + glEnable(GL_SCISSOR_TEST); 1.323 + glScissor(vp[0] + px, vp[1] + (vp[3] - py) - console_height, max_width, console_height); 1.324 + 1.325 + glBegin(GL_QUADS); 1.326 + glColor4f(0.1, 0.2, 0.5, 0.7); 1.327 + glVertex2f(0, -outbox_height); 1.328 + glVertex2f(max_width, -outbox_height); 1.329 + glVertex2f(max_width, 0); 1.330 + glVertex2f(0, 0); 1.331 + 1.332 + glColor4f(0.5, 0.2, 0.1, 0.7); 1.333 + glVertex2f(0, -console_height); 1.334 + glVertex2f(max_width, -console_height); 1.335 + glVertex2f(max_width, -outbox_height); 1.336 + glVertex2f(0, -outbox_height); 1.337 + glEnd(); 1.338 + 1.339 + // draw the output text 1.340 + glPushMatrix(); 1.341 + glTranslatef(0, -line_height, 0); 1.342 + glColor4f(1, 1, 1, 0.7); 1.343 + dtx_string(buflines.c_str()); 1.344 + glPopMatrix(); 1.345 + 1.346 + // draw the input line 1.347 + glTranslatef(0, -outbox_height - line_height, 0); 1.348 + 1.349 + std::string inpline = prompt + input.substr(input_win); 1.350 + dtx_string(inpline.c_str()); 1.351 + glDisable(GL_TEXTURE_2D); 1.352 + 1.353 + float cursor_x = dtx_char_pos(inpline.c_str(), cursor - input_win + prompt.size()); 1.354 + glBegin(GL_QUADS); 1.355 + glColor4f(1, 1, 1, 0.7); 1.356 + glVertex2f(cursor_x, -2); 1.357 + glVertex2f(cursor_x + 2, -2); 1.358 + glVertex2f(cursor_x + 2, line_height - 2); 1.359 + glVertex2f(cursor_x, line_height - 2); 1.360 + glEnd(); 1.361 + 1.362 + glPopAttrib(); 1.363 + 1.364 + glMatrixMode(GL_PROJECTION); 1.365 + glPopMatrix(); 1.366 + glMatrixMode(GL_MODELVIEW); 1.367 + glPopMatrix(); 1.368 +} 1.369 + 1.370 +void Console::input_key(int key) 1.371 +{ 1.372 + keybuf[inpq_back] = key; 1.373 + inpq_back = (inpq_back + 1) % INPUTQ_SIZE; 1.374 +} 1.375 + 1.376 +void Console::putchar(char c) 1.377 +{ 1.378 + if(output.empty()) { 1.379 + output.push_back(std::string("")); 1.380 + } 1.381 + 1.382 + if(c == '\n' || c == '\r') { 1.383 + output.push_back(std::string("")); 1.384 + if((int)output.size() > max_output_lines) { 1.385 + output.erase(output.begin()); 1.386 + } 1.387 + } else { 1.388 + output.back().push_back(c); 1.389 + } 1.390 +} 1.391 + 1.392 +void Console::puts(const char *str) 1.393 +{ 1.394 + while(*str) { 1.395 + putchar(*str++); 1.396 + } 1.397 +} 1.398 + 1.399 +void Console::printf(const char *fmt, ...) 1.400 +{ 1.401 + static char buf[1024]; 1.402 + va_list ap; 1.403 + 1.404 + va_start(ap, fmt); 1.405 + vsnprintf(buf, sizeof buf, fmt, ap); 1.406 + va_end(ap); 1.407 + 1.408 + puts(buf); 1.409 +} 1.410 + 1.411 +void Console::debug() 1.412 +{ 1.413 + fprintf(stderr, "visible: %s\n", visible ? "true" : "false"); 1.414 + fprintf(stderr, "size: %d lines x %d columns\n", nlines, ncolumns); 1.415 + fprintf(stderr, "cursor: %d\n", cursor); 1.416 + 1.417 + int qsize = 0; 1.418 + int qidx = inpq_front; 1.419 + while(qidx != inpq_back) { 1.420 + qsize++; 1.421 + qidx = (qidx + 1) % INPUTQ_SIZE; 1.422 + } 1.423 + 1.424 + fprintf(stderr, "input queue size: %d\n", qsize); 1.425 + fprintf(stderr, "current input line: \"%s\"\n", input.c_str()); 1.426 + fprintf(stderr, "%d saved inputs in the history (max %d)\n", (int)hist.size(), max_hist_lines); 1.427 + auto it = hist.begin(); 1.428 + int idx = 0; 1.429 + while(it != hist.end()) { 1.430 + fprintf(stderr, " h(%d): \"%s\"\n", idx++, it++->c_str()); 1.431 + } 1.432 + 1.433 + fprintf(stderr, "output buffer (lines: %d/%d):\n", (int)output.size(), max_output_lines); 1.434 + for(size_t i=0; i<output.size(); i++) { 1.435 + fprintf(stderr, "o(%d): \"%s\"\n", (int)i, output[i].c_str()); 1.436 + } 1.437 +}