erebus

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