vrheights

annotate src/console.cc @ 16:7f6d68d95c22

updated to new version of goatvr
author John Tsiombikas <nuclear@member.fsf.org>
date Fri, 30 Oct 2015 06:34:31 +0200
parents 053a52f0cb64
children
rev   line source
nuclear@5 1 #include <stdio.h>
nuclear@5 2 #include <stdlib.h>
nuclear@5 3 #include <string.h>
nuclear@5 4 #include <stdarg.h>
nuclear@5 5 #include <assert.h>
nuclear@5 6 #include <algorithm>
nuclear@5 7 #include "opengl.h"
nuclear@5 8 #include "console.h"
nuclear@5 9
nuclear@5 10 Console::Console()
nuclear@5 11 : prompt("> ")
nuclear@5 12 {
nuclear@5 13 visible = false;
nuclear@5 14 nlines = 16;
nuclear@5 15 ncolumns = 80;
nuclear@5 16 pos_x = pos_y = 0;
nuclear@5 17 anchor = TOP_LEFT;
nuclear@5 18
nuclear@5 19 inpq_front = inpq_back = 0;
nuclear@5 20
nuclear@5 21 max_hist_lines = 64;
nuclear@5 22 max_output_lines = 128;
nuclear@5 23
nuclear@5 24 hist_iter_valid = false;
nuclear@5 25
nuclear@5 26 echo = true;
nuclear@5 27 font = 0;
nuclear@5 28 font_size = 0;
nuclear@5 29
nuclear@5 30 cursor = 0;
nuclear@5 31 input_win = 0;
nuclear@5 32 }
nuclear@5 33
nuclear@5 34 void Console::set_cursor(int x)
nuclear@5 35 {
nuclear@5 36 if(x < 0 || x > (int)input.size()) {
nuclear@5 37 return;
nuclear@5 38 }
nuclear@5 39 cursor = x;
nuclear@5 40
nuclear@5 41 int max_chars = ncolumns - (int)prompt.size() - 1;
nuclear@5 42
nuclear@5 43 if(cursor < input_win) {
nuclear@5 44 input_win = cursor;
nuclear@5 45 } else if(cursor > input_win + max_chars) {
nuclear@5 46 input_win = std::max(cursor - max_chars, 0);
nuclear@5 47 }
nuclear@5 48 }
nuclear@5 49
nuclear@5 50 int Console::get_cursor() const
nuclear@5 51 {
nuclear@5 52 return cursor;
nuclear@5 53 }
nuclear@5 54
nuclear@5 55 void Console::set_font(dtx_font *font, int sz)
nuclear@5 56 {
nuclear@5 57 this->font = font;
nuclear@5 58 font_size = sz;
nuclear@5 59 }
nuclear@5 60
nuclear@5 61 void Console::set_history_size(int hsz)
nuclear@5 62 {
nuclear@5 63 max_hist_lines = hsz;
nuclear@5 64
nuclear@5 65 int drop = hist.size() - hsz;
nuclear@5 66 if(drop > 0) {
nuclear@5 67 auto it = hist.begin();
nuclear@5 68 for(int i=0; i<drop; i++) {
nuclear@5 69 ++it;
nuclear@5 70 }
nuclear@5 71 hist.erase(hist.begin(), it);
nuclear@5 72 }
nuclear@5 73 }
nuclear@5 74
nuclear@5 75 void Console::set_output_buffer_size(int sz)
nuclear@5 76 {
nuclear@5 77 max_output_lines = sz;
nuclear@5 78
nuclear@5 79 int drop = output.size() - sz;
nuclear@5 80 if(drop > 0) {
nuclear@5 81 output.erase(output.begin(), output.begin() + drop);
nuclear@5 82 }
nuclear@5 83 }
nuclear@5 84
nuclear@5 85 void Console::set_command_func(std::function<void (const char*)> func)
nuclear@5 86 {
nuclear@5 87 cmd_handler = func;
nuclear@5 88 }
nuclear@5 89
nuclear@5 90 void Console::set_echo(bool echo)
nuclear@5 91 {
nuclear@5 92 this->echo = echo;
nuclear@5 93 }
nuclear@5 94
nuclear@5 95 bool Console::get_echo() const
nuclear@5 96 {
nuclear@5 97 return echo;
nuclear@5 98 }
nuclear@5 99
nuclear@5 100 void Console::set_visible(bool v)
nuclear@5 101 {
nuclear@5 102 visible = v;
nuclear@5 103 }
nuclear@5 104
nuclear@5 105 bool Console::is_visible() const
nuclear@5 106 {
nuclear@5 107 return visible;
nuclear@5 108 }
nuclear@5 109
nuclear@5 110 void Console::set_size(int lines, int columns)
nuclear@5 111 {
nuclear@5 112 nlines = lines;
nuclear@5 113 ncolumns = columns;
nuclear@5 114 }
nuclear@5 115
nuclear@5 116 int Console::get_size_lines() const
nuclear@5 117 {
nuclear@5 118 return nlines;
nuclear@5 119 }
nuclear@5 120
nuclear@5 121 int Console::get_size_columns() const
nuclear@5 122 {
nuclear@5 123 return ncolumns;
nuclear@5 124 }
nuclear@5 125
nuclear@5 126 void Console::set_position(int x, int y, Anchor anchor)
nuclear@5 127 {
nuclear@5 128 pos_x = x;
nuclear@5 129 pos_y = y;
nuclear@5 130 this->anchor = anchor;
nuclear@5 131 }
nuclear@5 132
nuclear@5 133 bool Console::update()
nuclear@5 134 {
nuclear@5 135 bool must_redraw = false;
nuclear@5 136
nuclear@5 137 while(inpq_front != inpq_back) {
nuclear@5 138 int c = keybuf[inpq_front];
nuclear@5 139 inpq_front = (inpq_front + 1) % INPUTQ_SIZE;
nuclear@5 140
nuclear@5 141 switch(c) {
nuclear@5 142 case '\n':
nuclear@5 143 case '\r':
nuclear@5 144 if(echo) {
nuclear@5 145 puts(input.c_str());
nuclear@5 146 putchar('\n');
nuclear@5 147 }
nuclear@5 148 if(!input.empty() && cmd_handler) {
nuclear@5 149 cmd_handler(input.c_str());
nuclear@5 150 }
nuclear@5 151
nuclear@5 152 hist.push_back(std::move(input)); // move the input string into the history buffer
nuclear@5 153 if((int)hist.size() > max_hist_lines) {
nuclear@5 154 hist.pop_front();
nuclear@5 155 }
nuclear@5 156 hist_iter_valid = false;
nuclear@5 157
nuclear@5 158 set_cursor(0);
nuclear@5 159 must_redraw = true;
nuclear@5 160 break;
nuclear@5 161
nuclear@5 162 case '\t':
nuclear@5 163 // TODO: completion
nuclear@5 164 break;
nuclear@5 165
nuclear@5 166 case '\b':
nuclear@5 167 if(!input.empty()) {
nuclear@5 168 if(cursor == (int)input.size()) {
nuclear@5 169 input.pop_back();
nuclear@5 170 set_cursor(get_cursor() - 1);
nuclear@5 171 must_redraw = true;
nuclear@5 172 } else if(cursor > 0) {
nuclear@5 173 input.erase(cursor - 1, 1);
nuclear@5 174 set_cursor(get_cursor() - 1);
nuclear@5 175 must_redraw = true;
nuclear@5 176 }
nuclear@5 177 }
nuclear@5 178 break;
nuclear@5 179
nuclear@5 180 case KEY_UP:
nuclear@5 181 if(!hist.empty()) {
nuclear@5 182 if(!hist_iter_valid) {
nuclear@5 183 hist_iter = hist.rbegin();
nuclear@5 184 hist_iter_valid = true;
nuclear@5 185 input = *hist_iter;
nuclear@5 186 } else {
nuclear@5 187 if(++hist_iter == hist.rend()) {
nuclear@5 188 --hist_iter;
nuclear@5 189 break;
nuclear@5 190 }
nuclear@5 191 input = *hist_iter;
nuclear@5 192 }
nuclear@5 193 set_cursor(input.size());
nuclear@5 194 }
nuclear@5 195 break;
nuclear@5 196
nuclear@5 197 case KEY_DOWN:
nuclear@5 198 if(!hist.empty()) {
nuclear@5 199 if(!hist_iter_valid) {
nuclear@5 200 hist_iter = hist.rbegin();
nuclear@5 201 hist_iter_valid = true;
nuclear@5 202 }
nuclear@5 203 if(hist_iter != hist.rbegin()) {
nuclear@5 204 input = *--hist_iter;
nuclear@5 205 set_cursor(input.size());
nuclear@5 206 }
nuclear@5 207 }
nuclear@5 208 break;
nuclear@5 209
nuclear@5 210 case KEY_LEFT:
nuclear@5 211 if(cursor > 0) {
nuclear@5 212 set_cursor(get_cursor() - 1);
nuclear@5 213 must_redraw = true;
nuclear@5 214 }
nuclear@5 215 break;
nuclear@5 216
nuclear@5 217 case KEY_RIGHT:
nuclear@5 218 if(cursor < (int)input.size()) {
nuclear@5 219 set_cursor(get_cursor() + 1);
nuclear@5 220 must_redraw = true;
nuclear@5 221 }
nuclear@5 222 break;
nuclear@5 223
nuclear@5 224 case KEY_HOME:
nuclear@5 225 set_cursor(0);
nuclear@5 226 must_redraw = true;
nuclear@5 227 break;
nuclear@5 228
nuclear@5 229 case KEY_END:
nuclear@5 230 set_cursor(input.size());
nuclear@5 231 must_redraw = true;
nuclear@5 232 break;
nuclear@5 233
nuclear@5 234 case KEY_PGUP:
nuclear@5 235 case KEY_PGDOWN:
nuclear@5 236 // TODO scroll output buffer
nuclear@5 237 break;
nuclear@5 238
nuclear@5 239 default:
nuclear@5 240 if(c < 256 && isprint(c)) {
nuclear@5 241 if(cursor == (int)input.size()) {
nuclear@5 242 input.push_back(c);
nuclear@5 243 } else {
nuclear@5 244 input.insert(cursor, 1, c);
nuclear@5 245 }
nuclear@5 246 set_cursor(get_cursor() + 1);
nuclear@5 247 must_redraw = true;
nuclear@5 248 }
nuclear@5 249 }
nuclear@5 250 }
nuclear@5 251 return must_redraw;
nuclear@5 252 }
nuclear@5 253
nuclear@5 254 void Console::draw() const
nuclear@5 255 {
nuclear@5 256 if(!font || !visible) return;
nuclear@5 257 dtx_use_font(font, font_size);
nuclear@5 258
nuclear@5 259 std::string buflines;
nuclear@5 260
nuclear@5 261 int num_lines = std::min(nlines, (int)output.size());
nuclear@5 262 auto it = output.cbegin() + (output.size() - num_lines);
nuclear@5 263
nuclear@5 264 float max_width = dtx_glyph_width('Q') * ncolumns;
nuclear@5 265
nuclear@5 266 while(it != output.cend()) {
nuclear@5 267 const std::string &line = *it++;
nuclear@5 268 buflines += line + std::string("\n");
nuclear@5 269
nuclear@5 270 //max_width = std::max(max_width, dtx_string_width(line.c_str()));
nuclear@5 271 }
nuclear@5 272
nuclear@5 273 // draw the output box
nuclear@5 274 float line_height = dtx_line_height();
nuclear@5 275 float outbox_height = nlines * line_height;
nuclear@5 276 float console_height = outbox_height + line_height * 1.5;
nuclear@5 277
nuclear@5 278 int vp[4];
nuclear@5 279 glGetIntegerv(GL_VIEWPORT, vp);
nuclear@5 280
nuclear@5 281 int px, py;
nuclear@5 282 switch(anchor) {
nuclear@5 283 case TOP_LEFT:
nuclear@5 284 px = pos_x;
nuclear@5 285 py = pos_y;
nuclear@5 286 break;
nuclear@5 287
nuclear@5 288 case BOTTOM_LEFT:
nuclear@5 289 px = pos_x;
nuclear@5 290 py = vp[3] - console_height - pos_y;
nuclear@5 291 break;
nuclear@5 292
nuclear@5 293 case CENTER:
nuclear@5 294 px = (vp[2] - max_width) / 2 + pos_x;
nuclear@5 295 py = (vp[3] - console_height) / 2 + pos_y;
nuclear@5 296 break;
nuclear@5 297 }
nuclear@5 298
nuclear@5 299 glMatrixMode(GL_PROJECTION);
nuclear@5 300 glPushMatrix();
nuclear@5 301 glLoadIdentity();
nuclear@5 302 glOrtho(0, vp[2], 0, vp[3], -1, 1);
nuclear@5 303
nuclear@5 304 glMatrixMode(GL_MODELVIEW);
nuclear@5 305 glPushMatrix();
nuclear@5 306 glLoadIdentity();
nuclear@5 307 glTranslatef(px, vp[3] - py, 0);
nuclear@5 308
nuclear@5 309
nuclear@5 310 glPushAttrib(GL_ENABLE_BIT);
nuclear@5 311 glDisable(GL_DEPTH_TEST);
nuclear@5 312 glDisable(GL_LIGHTING);
nuclear@5 313 glDisable(GL_STENCIL_TEST);
nuclear@5 314 glEnable(GL_BLEND);
nuclear@5 315 glDisable(GL_TEXTURE_2D);
nuclear@5 316 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
nuclear@5 317
nuclear@5 318 glEnable(GL_SCISSOR_TEST);
nuclear@5 319 glScissor(vp[0] + px, vp[1] + (vp[3] - py) - console_height, max_width, console_height);
nuclear@5 320
nuclear@5 321 glBegin(GL_QUADS);
nuclear@5 322 glColor4f(0.1, 0.2, 0.5, 0.7);
nuclear@5 323 glVertex2f(0, -outbox_height);
nuclear@5 324 glVertex2f(max_width, -outbox_height);
nuclear@5 325 glVertex2f(max_width, 0);
nuclear@5 326 glVertex2f(0, 0);
nuclear@5 327
nuclear@5 328 glColor4f(0.5, 0.2, 0.1, 0.7);
nuclear@5 329 glVertex2f(0, -console_height);
nuclear@5 330 glVertex2f(max_width, -console_height);
nuclear@5 331 glVertex2f(max_width, -outbox_height);
nuclear@5 332 glVertex2f(0, -outbox_height);
nuclear@5 333 glEnd();
nuclear@5 334
nuclear@5 335 // draw the output text
nuclear@5 336 glPushMatrix();
nuclear@5 337 glTranslatef(0, -line_height, 0);
nuclear@5 338 glColor4f(1, 1, 1, 0.7);
nuclear@5 339 dtx_string(buflines.c_str());
nuclear@5 340 glPopMatrix();
nuclear@5 341
nuclear@5 342 // draw the input line
nuclear@5 343 glTranslatef(0, -outbox_height - line_height, 0);
nuclear@5 344
nuclear@5 345 std::string inpline = prompt + input.substr(input_win);
nuclear@5 346 dtx_string(inpline.c_str());
nuclear@5 347 glDisable(GL_TEXTURE_2D);
nuclear@5 348
nuclear@5 349 float cursor_x = dtx_char_pos(inpline.c_str(), cursor - input_win + prompt.size());
nuclear@5 350 glBegin(GL_QUADS);
nuclear@5 351 glColor4f(1, 1, 1, 0.7);
nuclear@5 352 glVertex2f(cursor_x, -2);
nuclear@5 353 glVertex2f(cursor_x + 2, -2);
nuclear@5 354 glVertex2f(cursor_x + 2, line_height - 2);
nuclear@5 355 glVertex2f(cursor_x, line_height - 2);
nuclear@5 356 glEnd();
nuclear@5 357
nuclear@5 358 glPopAttrib();
nuclear@5 359
nuclear@5 360 glMatrixMode(GL_PROJECTION);
nuclear@5 361 glPopMatrix();
nuclear@5 362 glMatrixMode(GL_MODELVIEW);
nuclear@5 363 glPopMatrix();
nuclear@5 364 }
nuclear@5 365
nuclear@5 366 void Console::input_key(int key)
nuclear@5 367 {
nuclear@5 368 keybuf[inpq_back] = key;
nuclear@5 369 inpq_back = (inpq_back + 1) % INPUTQ_SIZE;
nuclear@5 370 }
nuclear@5 371
nuclear@5 372 void Console::putchar(char c)
nuclear@5 373 {
nuclear@5 374 if(output.empty()) {
nuclear@5 375 output.push_back(std::string(""));
nuclear@5 376 }
nuclear@5 377
nuclear@5 378 if(c == '\n' || c == '\r') {
nuclear@5 379 output.push_back(std::string(""));
nuclear@5 380 if((int)output.size() > max_output_lines) {
nuclear@5 381 output.erase(output.begin());
nuclear@5 382 }
nuclear@5 383 } else {
nuclear@5 384 output.back().push_back(c);
nuclear@5 385 }
nuclear@5 386 }
nuclear@5 387
nuclear@5 388 void Console::puts(const char *str)
nuclear@5 389 {
nuclear@5 390 while(*str) {
nuclear@5 391 putchar(*str++);
nuclear@5 392 }
nuclear@5 393 }
nuclear@5 394
nuclear@5 395 void Console::printf(const char *fmt, ...)
nuclear@5 396 {
nuclear@5 397 static char buf[1024];
nuclear@5 398 va_list ap;
nuclear@5 399
nuclear@5 400 va_start(ap, fmt);
nuclear@5 401 vsnprintf(buf, sizeof buf, fmt, ap);
nuclear@5 402 va_end(ap);
nuclear@5 403
nuclear@5 404 puts(buf);
nuclear@5 405 }
nuclear@5 406
nuclear@5 407 void Console::debug()
nuclear@5 408 {
nuclear@5 409 fprintf(stderr, "visible: %s\n", visible ? "true" : "false");
nuclear@5 410 fprintf(stderr, "size: %d lines x %d columns\n", nlines, ncolumns);
nuclear@5 411 fprintf(stderr, "cursor: %d\n", cursor);
nuclear@5 412
nuclear@5 413 int qsize = 0;
nuclear@5 414 int qidx = inpq_front;
nuclear@5 415 while(qidx != inpq_back) {
nuclear@5 416 qsize++;
nuclear@5 417 qidx = (qidx + 1) % INPUTQ_SIZE;
nuclear@5 418 }
nuclear@5 419
nuclear@5 420 fprintf(stderr, "input queue size: %d\n", qsize);
nuclear@5 421 fprintf(stderr, "current input line: \"%s\"\n", input.c_str());
nuclear@5 422 fprintf(stderr, "%d saved inputs in the history (max %d)\n", (int)hist.size(), max_hist_lines);
nuclear@5 423 auto it = hist.begin();
nuclear@5 424 int idx = 0;
nuclear@5 425 while(it != hist.end()) {
nuclear@5 426 fprintf(stderr, " h(%d): \"%s\"\n", idx++, it++->c_str());
nuclear@5 427 }
nuclear@5 428
nuclear@5 429 fprintf(stderr, "output buffer (lines: %d/%d):\n", (int)output.size(), max_output_lines);
nuclear@5 430 for(size_t i=0; i<output.size(); i++) {
nuclear@5 431 fprintf(stderr, "o(%d): \"%s\"\n", (int)i, output[i].c_str());
nuclear@5 432 }
nuclear@5 433 }