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 +}