erebus

view src/console.cc @ 41:2e817711d0f6

console: proper input line windowing and clipping
author John Tsiombikas <nuclear@member.fsf.org>
date Tue, 10 Jun 2014 12:28:56 +0300
parents 9d6368850fe1
children b9294cd6b9dc
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 echo = true;
23 font = 0;
24 font_size = 0;
26 cursor = 0;
27 input_win = 0;
28 }
30 void Console::set_cursor(int x)
31 {
32 if(x < 0 || x > (int)input.size()) {
33 return;
34 }
35 cursor = x;
37 int max_chars = ncolumns - (int)prompt.size() - 1;
39 if(cursor < input_win) {
40 input_win = cursor;
41 } else if(cursor > input_win + max_chars) {
42 input_win = std::max(cursor - max_chars, 0);
43 }
44 }
46 int Console::get_cursor() const
47 {
48 return cursor;
49 }
51 void Console::set_font(dtx_font *font, int sz)
52 {
53 this->font = font;
54 font_size = sz;
55 }
57 void Console::set_history_size(int hsz)
58 {
59 max_hist_lines = hsz;
61 int drop = hist.size() - hsz;
62 if(drop > 0) {
63 auto it = hist.begin();
64 for(int i=0; i<drop; i++) {
65 ++it;
66 }
67 hist.erase(hist.begin(), it);
68 }
69 }
71 void Console::set_output_buffer_size(int sz)
72 {
73 max_output_lines = sz;
75 int drop = output.size() - sz;
76 if(drop > 0) {
77 output.erase(output.begin(), output.begin() + drop);
78 }
79 }
81 void Console::set_command_func(std::function<void (const char*)> func)
82 {
83 cmd_handler = func;
84 }
86 void Console::set_echo(bool echo)
87 {
88 this->echo = echo;
89 }
91 bool Console::get_echo() const
92 {
93 return echo;
94 }
96 void Console::set_visible(bool v)
97 {
98 visible = v;
99 }
101 bool Console::is_visible() const
102 {
103 return visible;
104 }
106 void Console::set_size(int lines, int columns)
107 {
108 nlines = lines;
109 ncolumns = columns;
110 }
112 int Console::get_size_lines() const
113 {
114 return nlines;
115 }
117 int Console::get_size_columns() const
118 {
119 return ncolumns;
120 }
122 bool Console::update()
123 {
124 bool must_redraw = false;
126 while(inpq_front != inpq_back) {
127 int c = keybuf[inpq_front];
128 inpq_front = (inpq_front + 1) % INPUTQ_SIZE;
130 switch(c) {
131 case '\n':
132 case '\r':
133 if(echo) {
134 puts(input.c_str());
135 putchar('\n');
136 }
137 if(!input.empty() && cmd_handler) {
138 cmd_handler(input.c_str());
139 }
141 hist.push_back(std::move(input)); // move the input string into the history buffer
142 if((int)hist.size() > max_hist_lines) {
143 hist.pop_front();
144 }
145 set_cursor(0);
146 must_redraw = true;
147 break;
149 case '\t':
150 // TODO: completion
151 break;
153 case '\b':
154 if(!input.empty()) {
155 if(cursor == (int)input.size()) {
156 input.pop_back();
157 set_cursor(get_cursor() - 1);
158 must_redraw = true;
159 } else if(cursor > 0) {
160 input.erase(cursor - 1, 1);
161 set_cursor(get_cursor() - 1);
162 must_redraw = true;
163 }
164 }
165 break;
167 case KEY_LEFT:
168 if(cursor > 0) {
169 set_cursor(get_cursor() - 1);
170 must_redraw = true;
171 }
172 break;
174 case KEY_RIGHT:
175 if(cursor < (int)input.size()) {
176 set_cursor(get_cursor() + 1);
177 must_redraw = true;
178 }
179 break;
181 case KEY_HOME:
182 set_cursor(0);
183 must_redraw = true;
184 break;
186 case KEY_END:
187 set_cursor(input.size());
188 must_redraw = true;
189 break;
191 case KEY_PGUP:
192 case KEY_PGDOWN:
193 // TODO history
194 break;
196 default:
197 if(c < 256 && isprint(c)) {
198 if(cursor == (int)input.size()) {
199 input.push_back(c);
200 } else {
201 input.insert(cursor, 1, c);
202 }
203 set_cursor(get_cursor() + 1);
204 must_redraw = true;
205 }
206 }
207 }
208 return must_redraw;
209 }
211 void Console::draw() const
212 {
213 if(!font || !visible) return;
214 dtx_use_font(font, font_size);
216 int vp[4];
217 glGetIntegerv(GL_VIEWPORT, vp);
219 glMatrixMode(GL_PROJECTION);
220 glPushMatrix();
221 glLoadIdentity();
222 glOrtho(vp[0], vp[0] + vp[2], vp[1], vp[1] + vp[3], -1, 1);
224 glMatrixMode(GL_MODELVIEW);
225 glPushMatrix();
226 glLoadIdentity();
227 glTranslatef(0, vp[3], 0);
229 std::string buflines;
231 int num_lines = std::min(nlines, (int)output.size());
232 auto it = output.cbegin() + (output.size() - num_lines);
234 float max_width = dtx_glyph_width('Q') * ncolumns;
236 while(it != output.cend()) {
237 const std::string &line = *it++;
238 buflines += line + std::string("\n");
240 //max_width = std::max(max_width, dtx_string_width(line.c_str()));
241 }
243 // draw the output box
244 float line_height = dtx_line_height();
245 float outbox_height = nlines * line_height;
246 float console_height = outbox_height + line_height * 1.5;
248 glPushAttrib(GL_ENABLE_BIT);
249 glEnable(GL_BLEND);
250 glDisable(GL_TEXTURE_2D);
251 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
253 glEnable(GL_SCISSOR_TEST);
254 glScissor(0, vp[3] - console_height, max_width, console_height);
256 glBegin(GL_QUADS);
257 glColor4f(0.1, 0.2, 0.5, 0.7);
258 glVertex2f(0, -outbox_height);
259 glVertex2f(max_width, -outbox_height);
260 glVertex2f(max_width, 0);
261 glVertex2f(0, 0);
263 glColor4f(0.5, 0.2, 0.1, 0.7);
264 glVertex2f(0, -console_height);
265 glVertex2f(max_width, -console_height);
266 glVertex2f(max_width, -outbox_height);
267 glVertex2f(0, -outbox_height);
268 glEnd();
270 // draw the output text
271 glPushMatrix();
272 glTranslatef(0, -line_height, 0);
273 glColor4f(1, 1, 1, 0.7);
274 dtx_string(buflines.c_str());
275 glPopMatrix();
277 // draw the input line
278 glTranslatef(0, -outbox_height - line_height, 0);
280 std::string inpline = prompt + input.substr(input_win);
281 dtx_string(inpline.c_str());
282 glDisable(GL_TEXTURE_2D);
284 float cursor_x = dtx_char_pos(inpline.c_str(), cursor - input_win + prompt.size());
285 glBegin(GL_QUADS);
286 glColor4f(1, 1, 1, 0.7);
287 glVertex2f(cursor_x, 0);
288 glVertex2f(cursor_x + 2, 0);
289 glVertex2f(cursor_x + 2, line_height - 2);
290 glVertex2f(cursor_x, line_height - 2);
291 glEnd();
293 glPopAttrib();
295 glMatrixMode(GL_PROJECTION);
296 glPopMatrix();
297 glMatrixMode(GL_MODELVIEW);
298 glPopMatrix();
299 }
301 void Console::input_key(int key)
302 {
303 keybuf[inpq_back] = key;
304 inpq_back = (inpq_back + 1) % INPUTQ_SIZE;
305 }
307 void Console::putchar(char c)
308 {
309 if(output.empty()) {
310 output.push_back(std::string(""));
311 }
313 if(c == '\n' || c == '\r') {
314 output.push_back(std::string(""));
315 if((int)output.size() > max_output_lines) {
316 output.erase(output.begin());
317 }
318 } else {
319 output.back().push_back(c);
320 }
321 }
323 void Console::puts(const char *str)
324 {
325 while(*str) {
326 putchar(*str++);
327 }
328 }
330 void Console::printf(const char *fmt, ...)
331 {
332 static char buf[1024];
333 va_list ap;
335 va_start(ap, fmt);
336 vsnprintf(buf, sizeof buf, fmt, ap);
337 va_end(ap);
339 puts(buf);
340 }
342 void Console::debug()
343 {
344 fprintf(stderr, "visible: %s\n", visible ? "true" : "false");
345 fprintf(stderr, "size: %d lines x %d columns\n", nlines, ncolumns);
346 fprintf(stderr, "cursor: %d\n", cursor);
348 int qsize = 0;
349 int qidx = inpq_front;
350 while(qidx != inpq_back) {
351 qsize++;
352 qidx = (qidx + 1) % INPUTQ_SIZE;
353 }
355 fprintf(stderr, "input queue size: %d\n", qsize);
356 fprintf(stderr, "current input line: \"%s\"\n", input.c_str());
357 fprintf(stderr, "%d saved inputs in the history (max %d)\n", (int)hist.size(), max_hist_lines);
358 auto it = hist.begin();
359 int idx = 0;
360 while(it != hist.end()) {
361 fprintf(stderr, " h(%d): \"%s\"\n", idx++, it++->c_str());
362 }
364 fprintf(stderr, "output buffer (lines: %d/%d):\n", (int)output.size(), max_output_lines);
365 for(size_t i=0; i<output.size(); i++) {
366 fprintf(stderr, "o(%d): \"%s\"\n", (int)i, output[i].c_str());
367 }
368 }