erebus

annotate src/console.cc @ 38:5e27c85e79ca

cursor handling in the console
author John Tsiombikas <nuclear@member.fsf.org>
date Mon, 09 Jun 2014 18:40:30 +0300
parents db8a90307386
children 9d6368850fe1
rev   line source
nuclear@37 1 #include <stdio.h>
nuclear@37 2 #include <stdlib.h>
nuclear@37 3 #include <string.h>
nuclear@37 4 #include <stdarg.h>
nuclear@37 5 #include <assert.h>
nuclear@37 6 #include <algorithm>
nuclear@37 7 #include "opengl.h"
nuclear@37 8 #include "console.h"
nuclear@37 9
nuclear@37 10 Console::Console()
nuclear@37 11 {
nuclear@37 12 visible = false;
nuclear@37 13 nlines = 16;
nuclear@37 14 ncolumns = 80;
nuclear@37 15
nuclear@37 16 inpq_front = inpq_back = 0;
nuclear@37 17
nuclear@37 18 max_hist_lines = 64;
nuclear@37 19 max_output_lines = 128;
nuclear@37 20
nuclear@37 21 echo = true;
nuclear@37 22 font = 0;
nuclear@37 23 font_size = 0;
nuclear@37 24
nuclear@37 25 cursor = 0;
nuclear@37 26 }
nuclear@37 27
nuclear@37 28 void Console::set_font(dtx_font *font, int sz)
nuclear@37 29 {
nuclear@37 30 this->font = font;
nuclear@37 31 font_size = sz;
nuclear@37 32 }
nuclear@37 33
nuclear@37 34 void Console::set_history_size(int hsz)
nuclear@37 35 {
nuclear@37 36 max_hist_lines = hsz;
nuclear@37 37
nuclear@37 38 int drop = hist.size() - hsz;
nuclear@37 39 if(drop > 0) {
nuclear@37 40 auto it = hist.begin();
nuclear@37 41 for(int i=0; i<drop; i++) {
nuclear@37 42 ++it;
nuclear@37 43 }
nuclear@37 44 hist.erase(hist.begin(), it);
nuclear@37 45 }
nuclear@37 46 }
nuclear@37 47
nuclear@37 48 void Console::set_output_buffer_size(int sz)
nuclear@37 49 {
nuclear@37 50 max_output_lines = sz;
nuclear@37 51
nuclear@37 52 int drop = output.size() - sz;
nuclear@37 53 if(drop > 0) {
nuclear@37 54 output.erase(output.begin(), output.begin() + drop);
nuclear@37 55 }
nuclear@37 56 }
nuclear@37 57
nuclear@37 58 void Console::set_command_func(std::function<void (const char*)> func)
nuclear@37 59 {
nuclear@37 60 cmd_handler = func;
nuclear@37 61 }
nuclear@37 62
nuclear@37 63 void Console::set_echo(bool echo)
nuclear@37 64 {
nuclear@37 65 this->echo = echo;
nuclear@37 66 }
nuclear@37 67
nuclear@37 68 bool Console::get_echo() const
nuclear@37 69 {
nuclear@37 70 return echo;
nuclear@37 71 }
nuclear@37 72
nuclear@37 73 void Console::set_visible(bool v)
nuclear@37 74 {
nuclear@37 75 visible = v;
nuclear@37 76 }
nuclear@37 77
nuclear@37 78 bool Console::is_visible() const
nuclear@37 79 {
nuclear@37 80 return visible;
nuclear@37 81 }
nuclear@37 82
nuclear@37 83 void Console::set_size(int lines, int columns)
nuclear@37 84 {
nuclear@37 85 nlines = lines;
nuclear@37 86 ncolumns = columns;
nuclear@37 87 }
nuclear@37 88
nuclear@37 89 int Console::get_size_lines() const
nuclear@37 90 {
nuclear@37 91 return nlines;
nuclear@37 92 }
nuclear@37 93
nuclear@37 94 int Console::get_size_columns() const
nuclear@37 95 {
nuclear@37 96 return ncolumns;
nuclear@37 97 }
nuclear@37 98
nuclear@37 99 bool Console::update()
nuclear@37 100 {
nuclear@37 101 bool must_redraw = false;
nuclear@37 102
nuclear@37 103 while(inpq_front != inpq_back) {
nuclear@37 104 int c = keybuf[inpq_front];
nuclear@37 105 inpq_front = (inpq_front + 1) % INPUTQ_SIZE;
nuclear@37 106
nuclear@37 107 switch(c) {
nuclear@37 108 case '\n':
nuclear@37 109 case '\r':
nuclear@37 110 if(cmd_handler) {
nuclear@37 111 cmd_handler(input.c_str());
nuclear@37 112 }
nuclear@37 113
nuclear@37 114 if(echo) {
nuclear@37 115 puts(input.c_str());
nuclear@37 116 putchar('\n');
nuclear@37 117 }
nuclear@37 118
nuclear@37 119 hist.push_back(std::move(input)); // move the input string into the history buffer
nuclear@37 120 if((int)hist.size() > max_hist_lines) {
nuclear@37 121 hist.pop_front();
nuclear@37 122 }
nuclear@37 123 cursor = 0;
nuclear@37 124 must_redraw = true;
nuclear@37 125 break;
nuclear@37 126
nuclear@37 127 case '\t':
nuclear@37 128 // TODO: completion
nuclear@37 129 break;
nuclear@37 130
nuclear@37 131 case '\b':
nuclear@37 132 if(!input.empty()) {
nuclear@37 133 if(cursor == (int)input.size()) {
nuclear@37 134 input.pop_back();
nuclear@37 135 --cursor;
nuclear@37 136 must_redraw = true;
nuclear@37 137 } else if(cursor > 0) {
nuclear@38 138 input.erase(cursor - 1, 1);
nuclear@37 139 --cursor;
nuclear@37 140 must_redraw = true;
nuclear@37 141 }
nuclear@37 142 }
nuclear@37 143 break;
nuclear@37 144
nuclear@37 145 case KEY_LEFT:
nuclear@37 146 if(cursor > 0) {
nuclear@37 147 --cursor;
nuclear@37 148 must_redraw = true;
nuclear@37 149 }
nuclear@37 150 break;
nuclear@37 151
nuclear@37 152 case KEY_RIGHT:
nuclear@37 153 if(cursor < (int)input.size()) {
nuclear@37 154 ++cursor;
nuclear@37 155 must_redraw = true;
nuclear@37 156 }
nuclear@37 157 break;
nuclear@37 158
nuclear@37 159 case KEY_HOME:
nuclear@37 160 cursor = 0;
nuclear@37 161 must_redraw = true;
nuclear@37 162 break;
nuclear@37 163
nuclear@37 164 case KEY_END:
nuclear@37 165 cursor = input.size();
nuclear@37 166 must_redraw = true;
nuclear@37 167 break;
nuclear@37 168
nuclear@37 169 case KEY_PGUP:
nuclear@37 170 case KEY_PGDOWN:
nuclear@37 171 // TODO history
nuclear@37 172 break;
nuclear@37 173
nuclear@37 174 default:
nuclear@37 175 if(c < 256 && isprint(c)) {
nuclear@38 176 if(cursor == (int)input.size()) {
nuclear@38 177 input.push_back(c);
nuclear@38 178 } else {
nuclear@38 179 input.insert(cursor, 1, c);
nuclear@38 180 }
nuclear@37 181 ++cursor;
nuclear@37 182 must_redraw = true;
nuclear@37 183 }
nuclear@37 184 }
nuclear@37 185 }
nuclear@37 186 return must_redraw;
nuclear@37 187 }
nuclear@37 188
nuclear@37 189 void Console::draw() const
nuclear@37 190 {
nuclear@37 191 if(!font || !visible) return;
nuclear@37 192 dtx_use_font(font, font_size);
nuclear@37 193
nuclear@37 194 int vp[4];
nuclear@37 195 glGetIntegerv(GL_VIEWPORT, vp);
nuclear@37 196
nuclear@37 197 glMatrixMode(GL_PROJECTION);
nuclear@37 198 glPushMatrix();
nuclear@37 199 glLoadIdentity();
nuclear@37 200 glOrtho(vp[0], vp[0] + vp[2], vp[1], vp[1] + vp[3], -1, 1);
nuclear@37 201
nuclear@37 202 glMatrixMode(GL_MODELVIEW);
nuclear@37 203 glPushMatrix();
nuclear@37 204 glLoadIdentity();
nuclear@37 205 glTranslatef(0, vp[3] - vp[1], 0);
nuclear@37 206
nuclear@37 207 std::string buflines;
nuclear@37 208
nuclear@37 209 int num_lines = std::min(nlines, (int)output.size());
nuclear@37 210 auto it = output.cbegin() + (output.size() - num_lines);
nuclear@37 211
nuclear@37 212 float max_width = dtx_glyph_width('Q') * ncolumns;
nuclear@37 213
nuclear@37 214 while(it != output.cend()) {
nuclear@37 215 const std::string &line = *it++;
nuclear@37 216 buflines += line + std::string("\n");
nuclear@37 217
nuclear@37 218 max_width = std::max(max_width, dtx_string_width(line.c_str()));
nuclear@37 219 }
nuclear@37 220
nuclear@37 221 // draw the output box
nuclear@38 222 float line_height = dtx_line_height();
nuclear@38 223 float outbox_height = nlines * line_height;
nuclear@37 224
nuclear@37 225 glPushAttrib(GL_ENABLE_BIT);
nuclear@37 226 glEnable(GL_BLEND);
nuclear@37 227 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
nuclear@37 228
nuclear@37 229 glBegin(GL_QUADS);
nuclear@37 230 glColor4f(0.1, 0.2, 0.5, 0.7);
nuclear@37 231 glVertex2f(0, -outbox_height);
nuclear@37 232 glVertex2f(max_width, -outbox_height);
nuclear@37 233 glVertex2f(max_width, 0);
nuclear@37 234 glVertex2f(0, 0);
nuclear@37 235
nuclear@37 236 glColor4f(0.5, 0.2, 0.1, 0.7);
nuclear@38 237 glVertex2f(0, -(outbox_height + line_height));
nuclear@38 238 glVertex2f(max_width, -(outbox_height + line_height));
nuclear@37 239 glVertex2f(max_width, -outbox_height);
nuclear@37 240 glVertex2f(0, -outbox_height);
nuclear@37 241 glEnd();
nuclear@37 242
nuclear@37 243 // draw the output text
nuclear@37 244 glPushMatrix();
nuclear@38 245 glTranslatef(0, -line_height, 0);
nuclear@37 246 glColor4f(1, 1, 1, 0.7);
nuclear@37 247 dtx_string(buflines.c_str());
nuclear@37 248 glPopMatrix();
nuclear@37 249
nuclear@37 250 // draw the input line
nuclear@38 251 glTranslatef(0, -outbox_height - line_height, 0);
nuclear@38 252
nuclear@38 253 std::string inpline = std::string("> ") + input;
nuclear@38 254 dtx_string(inpline.c_str());
nuclear@38 255 glDisable(GL_TEXTURE_2D);
nuclear@38 256
nuclear@38 257 float cursor_x = dtx_char_pos(inpline.c_str(), cursor + 2);
nuclear@38 258 glBegin(GL_QUADS);
nuclear@38 259 glColor4f(1, 1, 1, 0.7);
nuclear@38 260 glVertex2f(cursor_x, 2);
nuclear@38 261 glVertex2f(cursor_x + 2, 2);
nuclear@38 262 glVertex2f(cursor_x + 2, line_height - 2);
nuclear@38 263 glVertex2f(cursor_x, line_height - 2);
nuclear@38 264 glEnd();
nuclear@37 265
nuclear@37 266 glPopAttrib();
nuclear@37 267
nuclear@37 268 glMatrixMode(GL_PROJECTION);
nuclear@37 269 glPopMatrix();
nuclear@37 270 glMatrixMode(GL_MODELVIEW);
nuclear@37 271 glPopMatrix();
nuclear@37 272 }
nuclear@37 273
nuclear@37 274 void Console::input_key(int key)
nuclear@37 275 {
nuclear@37 276 keybuf[inpq_back] = key;
nuclear@37 277 inpq_back = (inpq_back + 1) % INPUTQ_SIZE;
nuclear@37 278 }
nuclear@37 279
nuclear@37 280 void Console::putchar(char c)
nuclear@37 281 {
nuclear@37 282 if(output.empty()) {
nuclear@37 283 output.push_back(std::string(""));
nuclear@37 284 }
nuclear@37 285
nuclear@37 286 if(c == '\n' || c == '\r') {
nuclear@37 287 output.push_back(std::string(""));
nuclear@37 288 if((int)output.size() > max_output_lines) {
nuclear@37 289 output.erase(output.begin());
nuclear@37 290 }
nuclear@37 291 } else {
nuclear@37 292 output.back().push_back(c);
nuclear@37 293 }
nuclear@37 294 }
nuclear@37 295
nuclear@37 296 void Console::puts(const char *str)
nuclear@37 297 {
nuclear@37 298 while(*str) {
nuclear@37 299 putchar(*str++);
nuclear@37 300 }
nuclear@37 301 }
nuclear@37 302
nuclear@37 303 void Console::printf(const char *fmt, ...)
nuclear@37 304 {
nuclear@37 305 static char buf[1024];
nuclear@37 306 va_list ap;
nuclear@37 307
nuclear@37 308 va_start(ap, fmt);
nuclear@37 309 vsnprintf(buf, sizeof buf, fmt, ap);
nuclear@37 310 va_end(ap);
nuclear@37 311
nuclear@37 312 puts(buf);
nuclear@37 313 }
nuclear@37 314
nuclear@37 315 void Console::debug()
nuclear@37 316 {
nuclear@37 317 fprintf(stderr, "visible: %s\n", visible ? "true" : "false");
nuclear@37 318 fprintf(stderr, "size: %d lines x %d columns\n", nlines, ncolumns);
nuclear@37 319 fprintf(stderr, "cursor: %d\n", cursor);
nuclear@37 320
nuclear@37 321 int qsize = 0;
nuclear@37 322 int qidx = inpq_front;
nuclear@37 323 while(qidx != inpq_back) {
nuclear@37 324 qsize++;
nuclear@37 325 qidx = (qidx + 1) % INPUTQ_SIZE;
nuclear@37 326 }
nuclear@37 327
nuclear@37 328 fprintf(stderr, "input queue size: %d\n", qsize);
nuclear@37 329 fprintf(stderr, "current input line: \"%s\"\n", input.c_str());
nuclear@37 330 fprintf(stderr, "%d saved inputs in the history (max %d)\n", (int)hist.size(), max_hist_lines);
nuclear@37 331 auto it = hist.begin();
nuclear@37 332 int idx = 0;
nuclear@37 333 while(it != hist.end()) {
nuclear@37 334 fprintf(stderr, " h(%d): \"%s\"\n", idx++, it++->c_str());
nuclear@37 335 }
nuclear@37 336
nuclear@37 337 fprintf(stderr, "output buffer (lines: %d/%d):\n", (int)output.size(), max_output_lines);
nuclear@37 338 for(size_t i=0; i<output.size(); i++) {
nuclear@37 339 fprintf(stderr, "o(%d): \"%s\"\n", (int)i, output[i].c_str());
nuclear@37 340 }
nuclear@37 341 }