erebus

view src/console.cc @ 42:b9294cd6b9dc

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