# HG changeset patch # User John Tsiombikas # Date 1402318860 -10800 # Node ID db8a9030738644d9c790d66cbc168781bccc397d # Parent 6eab83024d28168450bad9f187305af4b7870be4 implemented console and rudimentary commandline parser diff -r 6eab83024d28 -r db8a90307386 .clang_complete --- a/.clang_complete Mon Jun 09 07:28:03 2014 +0300 +++ b/.clang_complete Mon Jun 09 16:01:00 2014 +0300 @@ -1,2 +1,3 @@ -std=c++11 -Iliberebus/src +-I/usr/local/include diff -r 6eab83024d28 -r db8a90307386 data/mono.glyphmap Binary file data/mono.glyphmap has changed diff -r 6eab83024d28 -r db8a90307386 liberebus/src/erebus.cc --- a/liberebus/src/erebus.cc Mon Jun 09 07:28:03 2014 +0300 +++ b/liberebus/src/erebus.cc Mon Jun 09 16:01:00 2014 +0300 @@ -3,6 +3,11 @@ #include #include #include +#ifndef _MSC_VER +#include +#else +#include +#endif #include "erebus.h" #include "erebus_impl.h" #include "scene.h" @@ -248,8 +253,9 @@ int erb_load_scene(struct erebus *ctx, const char *fname) { - delete ctx->scn; - ctx->scn = new Scene; + if(!ctx->scn) { + ctx->scn = new Scene; + } if(!ctx->scn->load(fname)) { return -1; @@ -257,6 +263,29 @@ return 0; } +int erb_proc_cmd(struct erebus *ctx, const char *cmd) +{ + if(!ctx->scn) { + ctx->scn = new Scene; + } + + char *buf = (char*)alloca(strlen(cmd) + 1); + strcpy(buf, cmd); + + std::vector args; + char *tok; + while((tok = strtok(buf, " \t\v\n\r"))) { + buf = 0; + args.push_back(tok); + } + args.push_back(0); + + if(!ctx->scn->proc_cmd(args.size() - 1, &args[0])) { + return -1; + } + return 0; +} + bool erb_input_keyboard(struct erebus *ctx, int key, bool pressed) { if(!ctx) return false; diff -r 6eab83024d28 -r db8a90307386 liberebus/src/erebus.h --- a/liberebus/src/erebus.h Mon Jun 09 07:28:03 2014 +0300 +++ b/liberebus/src/erebus.h Mon Jun 09 16:01:00 2014 +0300 @@ -46,6 +46,7 @@ int erb_get_status(struct erebus *ctx, struct erb_render_status *stat); int erb_load_scene(struct erebus *ctx, const char *fname); +int erb_proc_cmd(struct erebus *ctx, const char *cmd); bool erb_input_keyboard(struct erebus *ctx, int key, bool pressed); bool erb_input_mouse_button(struct erebus *ctx, int bn, bool pressed, int x, int y); diff -r 6eab83024d28 -r db8a90307386 src/console.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/console.cc Mon Jun 09 16:01:00 2014 +0300 @@ -0,0 +1,324 @@ +#include +#include +#include +#include +#include +#include +#include "opengl.h" +#include "console.h" + +Console::Console() +{ + visible = false; + nlines = 16; + ncolumns = 80; + + inpq_front = inpq_back = 0; + + max_hist_lines = 64; + max_output_lines = 128; + + echo = true; + font = 0; + font_size = 0; + + cursor = 0; +} + +void Console::set_font(dtx_font *font, int sz) +{ + this->font = font; + font_size = sz; +} + +void Console::set_history_size(int hsz) +{ + max_hist_lines = hsz; + + int drop = hist.size() - hsz; + if(drop > 0) { + auto it = hist.begin(); + for(int i=0; i 0) { + output.erase(output.begin(), output.begin() + drop); + } +} + +void Console::set_command_func(std::function func) +{ + cmd_handler = func; +} + +void Console::set_echo(bool echo) +{ + this->echo = echo; +} + +bool Console::get_echo() const +{ + return echo; +} + +void Console::set_visible(bool v) +{ + visible = v; +} + +bool Console::is_visible() const +{ + return visible; +} + +void Console::set_size(int lines, int columns) +{ + nlines = lines; + ncolumns = columns; +} + +int Console::get_size_lines() const +{ + return nlines; +} + +int Console::get_size_columns() const +{ + return ncolumns; +} + +bool Console::update() +{ + bool must_redraw = false; + + while(inpq_front != inpq_back) { + int c = keybuf[inpq_front]; + inpq_front = (inpq_front + 1) % INPUTQ_SIZE; + + switch(c) { + case '\n': + case '\r': + if(cmd_handler) { + cmd_handler(input.c_str()); + } + + if(echo) { + puts(input.c_str()); + putchar('\n'); + } + + hist.push_back(std::move(input)); // move the input string into the history buffer + if((int)hist.size() > max_hist_lines) { + hist.pop_front(); + } + cursor = 0; + must_redraw = true; + break; + + case '\t': + // TODO: completion + break; + + case '\b': + if(!input.empty()) { + if(cursor == (int)input.size()) { + input.pop_back(); + --cursor; + must_redraw = true; + } else if(cursor > 0) { + input.erase(cursor - 1); + --cursor; + must_redraw = true; + } + } + break; + + case KEY_LEFT: + if(cursor > 0) { + --cursor; + must_redraw = true; + } + break; + + case KEY_RIGHT: + if(cursor < (int)input.size()) { + ++cursor; + must_redraw = true; + } + break; + + case KEY_HOME: + cursor = 0; + must_redraw = true; + break; + + case KEY_END: + cursor = input.size(); + must_redraw = true; + break; + + case KEY_PGUP: + case KEY_PGDOWN: + // TODO history + break; + + default: + if(c < 256 && isprint(c)) { + input.push_back(c); + ++cursor; + must_redraw = true; + } + } + } + return must_redraw; +} + +void Console::draw() const +{ + if(!font || !visible) return; + dtx_use_font(font, font_size); + + int vp[4]; + glGetIntegerv(GL_VIEWPORT, vp); + + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadIdentity(); + glOrtho(vp[0], vp[0] + vp[2], vp[1], vp[1] + vp[3], -1, 1); + + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); + glTranslatef(0, vp[3] - vp[1], 0); + + std::string buflines; + + int num_lines = std::min(nlines, (int)output.size()); + auto it = output.cbegin() + (output.size() - num_lines); + + float max_width = dtx_glyph_width('Q') * ncolumns; + + while(it != output.cend()) { + const std::string &line = *it++; + buflines += line + std::string("\n"); + + max_width = std::max(max_width, dtx_string_width(line.c_str())); + } + + // draw the output box + float outbox_height = nlines * dtx_line_height(); + + glPushAttrib(GL_ENABLE_BIT); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + glBegin(GL_QUADS); + glColor4f(0.1, 0.2, 0.5, 0.7); + glVertex2f(0, -outbox_height); + glVertex2f(max_width, -outbox_height); + glVertex2f(max_width, 0); + glVertex2f(0, 0); + + glColor4f(0.5, 0.2, 0.1, 0.7); + glVertex2f(0, -(outbox_height + dtx_line_height())); + glVertex2f(max_width, -(outbox_height + dtx_line_height())); + glVertex2f(max_width, -outbox_height); + glVertex2f(0, -outbox_height); + glEnd(); + + // draw the output text + glPushMatrix(); + glTranslatef(0, -dtx_line_height(), 0); + glColor4f(1, 1, 1, 0.7); + dtx_string(buflines.c_str()); + glPopMatrix(); + + // draw the input line + glTranslatef(0, -outbox_height - dtx_line_height(), 0); + dtx_string((std::string("> ") + input).c_str()); + + glPopAttrib(); + + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + glMatrixMode(GL_MODELVIEW); + glPopMatrix(); +} + +void Console::input_key(int key) +{ + keybuf[inpq_back] = key; + inpq_back = (inpq_back + 1) % INPUTQ_SIZE; +} + +void Console::putchar(char c) +{ + if(output.empty()) { + output.push_back(std::string("")); + } + + if(c == '\n' || c == '\r') { + output.push_back(std::string("")); + if((int)output.size() > max_output_lines) { + output.erase(output.begin()); + } + } else { + output.back().push_back(c); + } +} + +void Console::puts(const char *str) +{ + while(*str) { + putchar(*str++); + } +} + +void Console::printf(const char *fmt, ...) +{ + static char buf[1024]; + va_list ap; + + va_start(ap, fmt); + vsnprintf(buf, sizeof buf, fmt, ap); + va_end(ap); + + puts(buf); +} + +void Console::debug() +{ + fprintf(stderr, "visible: %s\n", visible ? "true" : "false"); + fprintf(stderr, "size: %d lines x %d columns\n", nlines, ncolumns); + fprintf(stderr, "cursor: %d\n", cursor); + + int qsize = 0; + int qidx = inpq_front; + while(qidx != inpq_back) { + qsize++; + qidx = (qidx + 1) % INPUTQ_SIZE; + } + + fprintf(stderr, "input queue size: %d\n", qsize); + fprintf(stderr, "current input line: \"%s\"\n", input.c_str()); + fprintf(stderr, "%d saved inputs in the history (max %d)\n", (int)hist.size(), max_hist_lines); + auto it = hist.begin(); + int idx = 0; + while(it != hist.end()) { + fprintf(stderr, " h(%d): \"%s\"\n", idx++, it++->c_str()); + } + + fprintf(stderr, "output buffer (lines: %d/%d):\n", (int)output.size(), max_output_lines); + for(size_t i=0; i +#include +#include +#include +#include + +#define INPUTQ_SIZE 64 + +class Console { +private: + bool visible; + int nlines, ncolumns; + + int inpq_front, inpq_back; + int keybuf[INPUTQ_SIZE]; + + std::string input; // current input line + int cursor; + + // input history + int max_hist_lines; + std::list hist; + + // console output + int max_output_lines; + std::vector output; + bool echo; + + std::function cmd_handler; + + int font_size; + dtx_font *font; + +public: + enum { + KEY_LEFT = 256, + KEY_RIGHT, + KEY_UP, KEY_DOWN, + KEY_INS, + KEY_DEL, + KEY_HOME, + KEY_END, + KEY_PGUP, + KEY_PGDOWN + }; + + Console(); + + void set_font(dtx_font *font, int sz); + void set_history_size(int hsz); + void set_output_buffer_size(int sz); + void set_command_func(std::function func); + + void set_echo(bool echo); + bool get_echo() const; + + void show() { set_visible(true); } + void hide() { set_visible(false); } + void set_visible(bool v); + bool is_visible() const; + + void set_size(int lines, int columns = 80); + int get_size_lines() const; + int get_size_columns() const; + + // update returns true if what it did makes a redraw necessary + bool update(); + void draw() const; + + void input_key(int key); // send a keystroke to the console + + void putchar(char c); + void puts(const char *str); + void printf(const char *fmt, ...); + + void debug(); +}; + +#endif // CONSOLE_H_ diff -r 6eab83024d28 -r db8a90307386 src/main.cc --- a/src/main.cc Mon Jun 09 07:28:03 2014 +0300 +++ b/src/main.cc Mon Jun 09 16:01:00 2014 +0300 @@ -9,6 +9,7 @@ #include #include "opengl.h" #include "erebus.h" +#include "console.h" using namespace std::chrono; @@ -25,6 +26,7 @@ static void reshape(int x, int y); static void keyb(unsigned char key, int x, int y); static void keyb_up(unsigned char key, int x, int y); +static void skeyb(int key, int x, int y); static void mouse(int bn, int st, int x, int y); static void motion(int x, int y); static void sball_button(int bn, int st); @@ -32,6 +34,7 @@ static int next_pow2(int x); static void sighandler(int s); static bool parse_args(int argc, char **argv); +static void con_parse(const char *line); static int win_width, win_height, width, height, rtex_width, rtex_height; static unsigned int rtex; @@ -48,7 +51,9 @@ static std::vector sfiles; +#define FONTSZ 22 static dtx_font *font; +static Console con; int main(int argc, char **argv) { @@ -66,6 +71,7 @@ glutReshapeFunc(reshape); glutKeyboardFunc(keyb); glutKeyboardUpFunc(keyb_up); + glutSpecialFunc(skeyb); glutMouseFunc(mouse); glutMotionFunc(motion); glutSpaceballButtonFunc(sball_button); @@ -89,11 +95,20 @@ width = glutGet(GLUT_WINDOW_WIDTH) / opt_imgscale; height = glutGet(GLUT_WINDOW_HEIGHT) / opt_imgscale; - //if(!(font = dtx_open_font("/usr/share/fonts/opentype/linux-libertine/LinLibertine_R.otf", 22))) { + //if(!(font = dtx_open_font("/usr/share/fonts/opentype/linux-libertine/LinLibertine_R.otf", FONTSZ))) { if(!(font = dtx_open_font_glyphmap("data/serif.glyphmap"))) { fprintf(stderr, "warning: failed to load font!\n"); } + //dtx_font *confont = dtx_open_font("/usr/share/fonts/truetype/droid/DroidSansMono.ttf", 14); + dtx_font *confont = dtx_open_font_glyphmap("data/mono.glyphmap"); + if(confont) { + con.set_font(confont, 14); + } else { + con.set_font(font, FONTSZ); + } + con.set_command_func(con_parse); + if(!(erb = erb_init())) { return false; } @@ -256,6 +271,10 @@ glTexCoord2f(0, 0); glVertex2f(-1, 1); glEnd(); + // draw the console + con.update(); + con.draw(); + // draw progress information etc... if(show_status) { display_statusbar(status); @@ -268,6 +287,7 @@ static void display_statusbar(const erb_render_status &status) { if(!font) return; + dtx_use_font(font, FONTSZ); bool show_progress = opt_samples > 0; @@ -368,22 +388,41 @@ { switch(key) { case 27: - end_frame(); - exit(0); + if(con.is_visible()) { + con.hide(); + glutPostRedisplay(); + } else { + end_frame(); + exit(0); + } + break; case ' ': - begin_frame(0); - break; - - case '\b': - printf("saving image.\n"); - save_image(); + if(!con.is_visible()) { + begin_frame(0); + } else { + con.input_key(' '); + glutPostRedisplay(); + } break; case '`': + con.set_visible(!con.is_visible()); + glutPostRedisplay(); + break; + + case '~': show_status = !show_status; glutPostRedisplay(); break; + + default: + // otherwise if the console is visible, let them through + if(con.is_visible()) { + con.input_key(key); + glutPostRedisplay(); + return; // don't pass anything to the erb input handler + } } if(erb_input_keyboard(erb, key, true)) { @@ -398,6 +437,55 @@ } } +static void skeyb(int key, int x, int y) +{ + if(key == GLUT_KEY_F12) { + printf("saving image...\n"); + save_image(); + return; + } + + if(con.is_visible()) { + switch(key) { + case GLUT_KEY_F8: + con.debug(); + return; + + case GLUT_KEY_LEFT: + con.input_key(Console::KEY_LEFT); + break; + case GLUT_KEY_RIGHT: + con.input_key(Console::KEY_RIGHT); + break; + case GLUT_KEY_UP: + con.input_key(Console::KEY_UP); + break; + case GLUT_KEY_DOWN: + con.input_key(Console::KEY_DOWN); + break; + case GLUT_KEY_HOME: + con.input_key(Console::KEY_HOME); + break; + case GLUT_KEY_END: + con.input_key(Console::KEY_END); + break; + case GLUT_KEY_INSERT: + con.input_key(Console::KEY_INS); + break; + case GLUT_KEY_PAGE_UP: + con.input_key(Console::KEY_PGUP); + break; + case GLUT_KEY_PAGE_DOWN: + con.input_key(Console::KEY_PGDOWN); + break; + + default: + return; + } + glutPostRedisplay(); + } +} + static void mouse(int bn, int st, int x, int y) { if(erb_input_mouse_button(erb, bn - GLUT_LEFT_BUTTON, st == GLUT_DOWN, x, y)) { @@ -483,3 +571,14 @@ return true; } + +static void con_parse(const char *line) +{ + printf("got line: %s\n", line); + + if(erb_proc_cmd(erb, line) == -1) { + con.puts("invalid command\n"); + } else { + begin_frame(0); + } +}