erebus

diff src/console.cc @ 37:db8a90307386

implemented console and rudimentary commandline parser
author John Tsiombikas <nuclear@member.fsf.org>
date Mon, 09 Jun 2014 16:01:00 +0300
parents
children 5e27c85e79ca
line diff
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/src/console.cc	Mon Jun 09 16:01:00 2014 +0300
     1.3 @@ -0,0 +1,324 @@
     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 +{
    1.15 +	visible = false;
    1.16 +	nlines = 16;
    1.17 +	ncolumns = 80;
    1.18 +
    1.19 +	inpq_front = inpq_back = 0;
    1.20 +
    1.21 +	max_hist_lines = 64;
    1.22 +	max_output_lines = 128;
    1.23 +
    1.24 +	echo = true;
    1.25 +	font = 0;
    1.26 +	font_size = 0;
    1.27 +
    1.28 +	cursor = 0;
    1.29 +}
    1.30 +
    1.31 +void Console::set_font(dtx_font *font, int sz)
    1.32 +{
    1.33 +	this->font = font;
    1.34 +	font_size = sz;
    1.35 +}
    1.36 +
    1.37 +void Console::set_history_size(int hsz)
    1.38 +{
    1.39 +	max_hist_lines = hsz;
    1.40 +
    1.41 +	int drop = hist.size() - hsz;
    1.42 +	if(drop > 0) {
    1.43 +		auto it = hist.begin();
    1.44 +		for(int i=0; i<drop; i++) {
    1.45 +			++it;
    1.46 +		}
    1.47 +		hist.erase(hist.begin(), it);
    1.48 +	}
    1.49 +}
    1.50 +
    1.51 +void Console::set_output_buffer_size(int sz)
    1.52 +{
    1.53 +	max_output_lines = sz;
    1.54 +
    1.55 +	int drop = output.size() - sz;
    1.56 +	if(drop > 0) {
    1.57 +		output.erase(output.begin(), output.begin() + drop);
    1.58 +	}
    1.59 +}
    1.60 +
    1.61 +void Console::set_command_func(std::function<void (const char*)> func)
    1.62 +{
    1.63 +	cmd_handler = func;
    1.64 +}
    1.65 +
    1.66 +void Console::set_echo(bool echo)
    1.67 +{
    1.68 +	this->echo = echo;
    1.69 +}
    1.70 +
    1.71 +bool Console::get_echo() const
    1.72 +{
    1.73 +	return echo;
    1.74 +}
    1.75 +
    1.76 +void Console::set_visible(bool v)
    1.77 +{
    1.78 +	visible = v;
    1.79 +}
    1.80 +
    1.81 +bool Console::is_visible() const
    1.82 +{
    1.83 +	return visible;
    1.84 +}
    1.85 +
    1.86 +void Console::set_size(int lines, int columns)
    1.87 +{
    1.88 +	nlines = lines;
    1.89 +	ncolumns = columns;
    1.90 +}
    1.91 +
    1.92 +int Console::get_size_lines() const
    1.93 +{
    1.94 +	return nlines;
    1.95 +}
    1.96 +
    1.97 +int Console::get_size_columns() const
    1.98 +{
    1.99 +	return ncolumns;
   1.100 +}
   1.101 +
   1.102 +bool Console::update()
   1.103 +{
   1.104 +	bool must_redraw = false;
   1.105 +
   1.106 +	while(inpq_front != inpq_back) {
   1.107 +		int c = keybuf[inpq_front];
   1.108 +		inpq_front = (inpq_front + 1) % INPUTQ_SIZE;
   1.109 +
   1.110 +		switch(c) {
   1.111 +		case '\n':
   1.112 +		case '\r':
   1.113 +			if(cmd_handler) {
   1.114 +				cmd_handler(input.c_str());
   1.115 +			}
   1.116 +
   1.117 +			if(echo) {
   1.118 +				puts(input.c_str());
   1.119 +				putchar('\n');
   1.120 +			}
   1.121 +
   1.122 +			hist.push_back(std::move(input));	// move the input string into the history buffer
   1.123 +			if((int)hist.size() > max_hist_lines) {
   1.124 +				hist.pop_front();
   1.125 +			}
   1.126 +			cursor = 0;
   1.127 +			must_redraw = true;
   1.128 +			break;
   1.129 +
   1.130 +		case '\t':
   1.131 +			// TODO: completion
   1.132 +			break;
   1.133 +
   1.134 +		case '\b':
   1.135 +			if(!input.empty()) {
   1.136 +				if(cursor == (int)input.size()) {
   1.137 +					input.pop_back();
   1.138 +					--cursor;
   1.139 +					must_redraw = true;
   1.140 +				} else if(cursor > 0) {
   1.141 +					input.erase(cursor - 1);
   1.142 +					--cursor;
   1.143 +					must_redraw = true;
   1.144 +				}
   1.145 +			}
   1.146 +			break;
   1.147 +
   1.148 +		case KEY_LEFT:
   1.149 +			if(cursor > 0) {
   1.150 +				--cursor;
   1.151 +				must_redraw = true;
   1.152 +			}
   1.153 +			break;
   1.154 +
   1.155 +		case KEY_RIGHT:
   1.156 +			if(cursor < (int)input.size()) {
   1.157 +				++cursor;
   1.158 +				must_redraw = true;
   1.159 +			}
   1.160 +			break;
   1.161 +
   1.162 +		case KEY_HOME:
   1.163 +			cursor = 0;
   1.164 +			must_redraw = true;
   1.165 +			break;
   1.166 +
   1.167 +		case KEY_END:
   1.168 +			cursor = input.size();
   1.169 +			must_redraw = true;
   1.170 +			break;
   1.171 +
   1.172 +		case KEY_PGUP:
   1.173 +		case KEY_PGDOWN:
   1.174 +			// TODO history
   1.175 +			break;
   1.176 +
   1.177 +		default:
   1.178 +			if(c < 256 && isprint(c)) {
   1.179 +				input.push_back(c);
   1.180 +				++cursor;
   1.181 +				must_redraw = true;
   1.182 +			}
   1.183 +		}
   1.184 +	}
   1.185 +	return must_redraw;
   1.186 +}
   1.187 +
   1.188 +void Console::draw() const
   1.189 +{
   1.190 +	if(!font || !visible) return;
   1.191 +	dtx_use_font(font, font_size);
   1.192 +
   1.193 +	int vp[4];
   1.194 +	glGetIntegerv(GL_VIEWPORT, vp);
   1.195 +
   1.196 +	glMatrixMode(GL_PROJECTION);
   1.197 +	glPushMatrix();
   1.198 +	glLoadIdentity();
   1.199 +	glOrtho(vp[0], vp[0] + vp[2], vp[1], vp[1] + vp[3], -1, 1);
   1.200 +
   1.201 +	glMatrixMode(GL_MODELVIEW);
   1.202 +	glPushMatrix();
   1.203 +	glLoadIdentity();
   1.204 +	glTranslatef(0, vp[3] - vp[1], 0);
   1.205 +
   1.206 +	std::string buflines;
   1.207 +
   1.208 +	int num_lines = std::min(nlines, (int)output.size());
   1.209 +	auto it = output.cbegin() + (output.size() - num_lines);
   1.210 +
   1.211 +	float max_width = dtx_glyph_width('Q') * ncolumns;
   1.212 +
   1.213 +	while(it != output.cend()) {
   1.214 +		const std::string &line = *it++;
   1.215 +		buflines += line + std::string("\n");
   1.216 +
   1.217 +		max_width = std::max(max_width, dtx_string_width(line.c_str()));
   1.218 +	}
   1.219 +
   1.220 +	// draw the output box
   1.221 +	float outbox_height = nlines * dtx_line_height();
   1.222 +
   1.223 +	glPushAttrib(GL_ENABLE_BIT);
   1.224 +	glEnable(GL_BLEND);
   1.225 +	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
   1.226 +
   1.227 +	glBegin(GL_QUADS);
   1.228 +	glColor4f(0.1, 0.2, 0.5, 0.7);
   1.229 +	glVertex2f(0, -outbox_height);
   1.230 +	glVertex2f(max_width, -outbox_height);
   1.231 +	glVertex2f(max_width, 0);
   1.232 +	glVertex2f(0, 0);
   1.233 +
   1.234 +	glColor4f(0.5, 0.2, 0.1, 0.7);
   1.235 +	glVertex2f(0, -(outbox_height + dtx_line_height()));
   1.236 +	glVertex2f(max_width, -(outbox_height + dtx_line_height()));
   1.237 +	glVertex2f(max_width, -outbox_height);
   1.238 +	glVertex2f(0, -outbox_height);
   1.239 +	glEnd();
   1.240 +
   1.241 +	// draw the output text
   1.242 +	glPushMatrix();
   1.243 +	glTranslatef(0, -dtx_line_height(), 0);
   1.244 +	glColor4f(1, 1, 1, 0.7);
   1.245 +	dtx_string(buflines.c_str());
   1.246 +	glPopMatrix();
   1.247 +
   1.248 +	// draw the input line
   1.249 +	glTranslatef(0, -outbox_height - dtx_line_height(), 0);
   1.250 +	dtx_string((std::string("> ") + input).c_str());
   1.251 +
   1.252 +	glPopAttrib();
   1.253 +
   1.254 +	glMatrixMode(GL_PROJECTION);
   1.255 +	glPopMatrix();
   1.256 +	glMatrixMode(GL_MODELVIEW);
   1.257 +	glPopMatrix();
   1.258 +}
   1.259 +
   1.260 +void Console::input_key(int key)
   1.261 +{
   1.262 +	keybuf[inpq_back] = key;
   1.263 +	inpq_back = (inpq_back + 1) % INPUTQ_SIZE;
   1.264 +}
   1.265 +
   1.266 +void Console::putchar(char c)
   1.267 +{
   1.268 +	if(output.empty()) {
   1.269 +		output.push_back(std::string(""));
   1.270 +	}
   1.271 +
   1.272 +	if(c == '\n' || c == '\r') {
   1.273 +		output.push_back(std::string(""));
   1.274 +		if((int)output.size() > max_output_lines) {
   1.275 +			output.erase(output.begin());
   1.276 +		}
   1.277 +	} else {
   1.278 +		output.back().push_back(c);
   1.279 +	}
   1.280 +}
   1.281 +
   1.282 +void Console::puts(const char *str)
   1.283 +{
   1.284 +	while(*str) {
   1.285 +		putchar(*str++);
   1.286 +	}
   1.287 +}
   1.288 +
   1.289 +void Console::printf(const char *fmt, ...)
   1.290 +{
   1.291 +	static char buf[1024];
   1.292 +	va_list ap;
   1.293 +
   1.294 +	va_start(ap, fmt);
   1.295 +	vsnprintf(buf, sizeof buf, fmt, ap);
   1.296 +	va_end(ap);
   1.297 +
   1.298 +	puts(buf);
   1.299 +}
   1.300 +
   1.301 +void Console::debug()
   1.302 +{
   1.303 +	fprintf(stderr, "visible: %s\n", visible ? "true" : "false");
   1.304 +	fprintf(stderr, "size: %d lines x %d columns\n", nlines, ncolumns);
   1.305 +	fprintf(stderr, "cursor: %d\n", cursor);
   1.306 +
   1.307 +	int qsize = 0;
   1.308 +	int qidx = inpq_front;
   1.309 +	while(qidx != inpq_back) {
   1.310 +		qsize++;
   1.311 +		qidx = (qidx + 1) % INPUTQ_SIZE;
   1.312 +	}
   1.313 +
   1.314 +	fprintf(stderr, "input queue size: %d\n", qsize);
   1.315 +	fprintf(stderr, "current input line: \"%s\"\n", input.c_str());
   1.316 +	fprintf(stderr, "%d saved inputs in the history (max %d)\n", (int)hist.size(), max_hist_lines);
   1.317 +	auto it = hist.begin();
   1.318 +	int idx = 0;
   1.319 +	while(it != hist.end()) {
   1.320 +		fprintf(stderr, "  h(%d): \"%s\"\n", idx++, it++->c_str());
   1.321 +	}
   1.322 +
   1.323 +	fprintf(stderr, "output buffer (lines: %d/%d):\n", (int)output.size(), max_output_lines);
   1.324 +	for(size_t i=0; i<output.size(); i++) {
   1.325 +		fprintf(stderr, "o(%d): \"%s\"\n", (int)i, output[i].c_str());
   1.326 +	}
   1.327 +}