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