From 89a70f398a0cc8440dcb25c3385c021ff92c739f Mon Sep 17 00:00:00 2001 From: "Zed A. Shaw" Date: Sat, 16 Nov 2024 12:10:14 -0500 Subject: [PATCH] Rewrote the ansi parser to exactly callback on color setting, so now just need to clean this all up and fix a few little bugs. --- ansi_parser.cpp | 185 ++++++++++++++++++++++++++---------------- ansi_parser.hpp | 6 +- ansi_parser.rl | 47 +++++++++-- gui.cpp | 16 +--- main.cpp | 3 + render.cpp | 125 +++++++++++++++++++++------- render.hpp | 5 +- status.txt | 5 +- tests/ansi_parser.cpp | 6 +- 9 files changed, 273 insertions(+), 125 deletions(-) diff --git a/ansi_parser.cpp b/ansi_parser.cpp index d33c9e5..e490c63 100644 --- a/ansi_parser.cpp +++ b/ansi_parser.cpp @@ -5,86 +5,98 @@ #include "dbc.hpp" #include #include "ansi_parser.hpp" +#include using namespace fmt; -#line 80 "ansi_parser.rl" +#line 105 "ansi_parser.rl" -#line 12 "ansi_parser.cpp" +#line 13 "ansi_parser.cpp" static const char _foo_actions[] = { 0, 1, 0, 1, 3, 1, 4, 1, - 5, 1, 6, 1, 7, 1, 11, 1, - 12, 2, 1, 8, 2, 1, 9, 2, - 12, 5, 3, 1, 10, 2 + 5, 1, 6, 1, 7, 1, 8, 1, + 9, 1, 10, 1, 14, 1, 15, 2, + 1, 11, 2, 1, 12, 2, 15, 5, + 3, 1, 13, 2 }; static const char _foo_key_offsets[] = { - 0, 0, 1, 2, 4, 6, 7, 8, - 9, 11, 14, 16, 19, 21, 24, 25, - 27, 28, 29, 30, 31 + 0, 0, 1, 2, 8, 9, 11, 12, + 15, 16, 17, 18, 20, 23, 25, 28, + 30, 33, 34, 37, 38, 39, 40, 41, + 42 }; static const int _foo_trans_keys[] = { - 27, 91, 51, 52, 56, 57, 59, 50, - 59, 48, 57, 59, 48, 57, 48, 57, - 59, 48, 57, 48, 57, 109, 48, 57, - 109, 56, 57, 59, 50, 109, 27, 27, - 0 + 27, 91, 50, 51, 52, 55, 48, 54, + 109, 55, 109, 109, 56, 57, 109, 59, + 50, 59, 48, 57, 59, 48, 57, 48, + 57, 59, 48, 57, 48, 57, 109, 48, + 57, 109, 56, 57, 109, 59, 50, 109, + 109, 27, 27, 0 }; static const char _foo_single_lengths[] = { - 0, 1, 1, 2, 2, 1, 1, 1, - 0, 1, 0, 1, 0, 1, 1, 2, - 1, 1, 1, 1, 1 + 0, 1, 1, 4, 1, 2, 1, 3, + 1, 1, 1, 0, 1, 0, 1, 0, + 1, 1, 3, 1, 1, 1, 1, 1, + 1 }; static const char _foo_range_lengths[] = { - 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0 + 0, 0, 0, 1, 0, 0, 0, 0, + 0, 0, 0, 1, 1, 1, 1, 1, + 1, 0, 0, 0, 0, 0, 0, 0, + 0 }; static const char _foo_index_offsets[] = { - 0, 0, 2, 4, 7, 10, 12, 14, - 16, 18, 21, 23, 26, 28, 31, 33, - 36, 38, 40, 42, 44 + 0, 0, 2, 4, 10, 12, 15, 17, + 21, 23, 25, 27, 29, 32, 34, 37, + 39, 42, 44, 48, 50, 52, 54, 56, + 58 }; static const char _foo_trans_targs[] = { - 2, 1, 3, 0, 4, 15, 0, 5, - 14, 0, 6, 0, 7, 0, 8, 0, - 9, 0, 10, 9, 0, 11, 0, 12, - 11, 0, 13, 0, 20, 13, 0, 20, - 0, 16, 18, 0, 17, 0, 7, 0, - 20, 0, 2, 1, 2, 1, 0 + 2, 1, 3, 0, 5, 7, 18, 22, + 4, 0, 24, 0, 6, 24, 0, 24, + 0, 8, 17, 24, 0, 9, 0, 10, + 0, 11, 0, 12, 0, 13, 12, 0, + 14, 0, 15, 14, 0, 16, 0, 24, + 16, 0, 24, 0, 19, 21, 24, 0, + 20, 0, 10, 0, 24, 0, 24, 0, + 2, 1, 2, 1, 0 }; static const char _foo_trans_actions[] = { - 0, 7, 0, 0, 13, 13, 0, 0, - 0, 0, 0, 0, 3, 0, 0, 0, - 1, 0, 17, 0, 0, 1, 0, 20, - 0, 0, 1, 0, 26, 0, 0, 9, - 0, 0, 0, 0, 0, 0, 5, 0, - 11, 0, 0, 7, 15, 23, 0 + 0, 7, 0, 0, 19, 19, 19, 19, + 19, 0, 0, 0, 0, 17, 0, 15, + 0, 0, 0, 0, 0, 0, 0, 3, + 0, 0, 0, 1, 0, 23, 0, 0, + 1, 0, 26, 0, 0, 1, 0, 32, + 0, 0, 9, 0, 0, 0, 0, 0, + 0, 0, 5, 0, 11, 0, 13, 0, + 0, 7, 21, 29, 0 }; static const char _foo_eof_actions[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 15 + 0, 0, 0, 0, 0, 0, 0, 0, + 21 }; -static const int foo_start = 19; -static const int foo_first_final = 19; +static const int foo_start = 23; +static const int foo_first_final = 23; static const int foo_error = 0; -static const int foo_en_main = 19; +static const int foo_en_main = 23; -#line 83 "ansi_parser.rl" +#line 108 "ansi_parser.rl" #include @@ -98,7 +110,7 @@ ANSIParser::ANSIParser(sf::Color default_fg, sf::Color default_bg) : } } -bool ANSIParser::parse(std::wstring_view codes, WriteCB write) { +bool ANSIParser::parse(std::wstring_view codes, ColorCB color_cb, WriteCB write_cb) { const wchar_t *start = NULL; int cs = 0; unsigned int value = 0; @@ -110,14 +122,14 @@ bool ANSIParser::parse(std::wstring_view codes, WriteCB write) { sf::Color &target = color; -#line 105 "ansi_parser.cpp" +#line 117 "ansi_parser.cpp" { cs = foo_start; } -#line 108 "ansi_parser.rl" +#line 133 "ansi_parser.rl" -#line 108 "ansi_parser.cpp" +#line 120 "ansi_parser.cpp" { int _klen; unsigned int _trans; @@ -191,13 +203,13 @@ _match: switch ( *_acts++ ) { case 0: -#line 13 "ansi_parser.rl" +#line 14 "ansi_parser.rl" { start = p; } break; case 1: -#line 17 "ansi_parser.rl" +#line 18 "ansi_parser.rl" { value = 0; size_t len = p - start; @@ -221,56 +233,79 @@ _match: } break; case 2: -#line 39 "ansi_parser.rl" - { } +#line 40 "ansi_parser.rl" + { + color_cb(color, bgcolor); + } break; case 3: -#line 40 "ansi_parser.rl" +#line 43 "ansi_parser.rl" { target = color; } break; case 4: -#line 43 "ansi_parser.rl" +#line 46 "ansi_parser.rl" { target = bgcolor; } break; case 5: -#line 47 "ansi_parser.rl" +#line 50 "ansi_parser.rl" { - write(bgcolor, color, (*p)); + write_cb((*p)); } break; case 6: -#line 51 "ansi_parser.rl" +#line 54 "ansi_parser.rl" { color = $default_fg; } break; case 7: -#line 52 "ansi_parser.rl" +#line 55 "ansi_parser.rl" { bgcolor = $default_bg; } break; case 8: -#line 54 "ansi_parser.rl" - { target.r = value; } +#line 56 "ansi_parser.rl" + { + color = {100,100,100}; // UNDO THIS + bgcolor = $default_bg; // THIS TOO + color_cb(color, bgcolor); + } break; case 9: -#line 55 "ansi_parser.rl" - { target.g = value; } +#line 61 "ansi_parser.rl" + { + color = $default_fg; + bgcolor = $default_bg; + color_cb(color, bgcolor); + } break; case 10: -#line 56 "ansi_parser.rl" - { target.b = value; } +#line 66 "ansi_parser.rl" + { + } break; case 11: -#line 57 "ansi_parser.rl" - { value = 0; } +#line 69 "ansi_parser.rl" + { target.r = value; } break; case 12: -#line 58 "ansi_parser.rl" - { } +#line 70 "ansi_parser.rl" + { target.g = value; } break; -#line 247 "ansi_parser.cpp" + case 13: +#line 71 "ansi_parser.rl" + { target.b = value; } + break; + case 14: +#line 72 "ansi_parser.rl" + { value = 0; } + break; + case 15: +#line 73 "ansi_parser.rl" + {} + break; +#line 279 "ansi_parser.cpp" } } @@ -286,11 +321,11 @@ _again: unsigned int __nacts = (unsigned int) *__acts++; while ( __nacts-- > 0 ) { switch ( *__acts++ ) { - case 12: -#line 58 "ansi_parser.rl" - { } + case 15: +#line 73 "ansi_parser.rl" + {} break; -#line 265 "ansi_parser.cpp" +#line 297 "ansi_parser.cpp" } } } @@ -298,7 +333,17 @@ _again: _out: {} } -#line 109 "ansi_parser.rl" +#line 134 "ansi_parser.rl" + + bool good = p - pe == 0; + + if(!good) { + println("FAIL AT {}", p - pe); + // dear cthuhlu, save me from the pain that is wstring + for(int i = 0; i < 100; i++) { + print("{}", char(p[i])); + } + } - return p - pe == 0; + return good; } diff --git a/ansi_parser.hpp b/ansi_parser.hpp index 77755c7..64ba529 100644 --- a/ansi_parser.hpp +++ b/ansi_parser.hpp @@ -3,7 +3,9 @@ #include #include -typedef std::function WriteCB; +typedef std::function ColorCB; + +typedef std::function WriteCB; class ANSIParser { sf::Color $default_fg; @@ -16,5 +18,5 @@ public: // disable copying ANSIParser(ANSIParser& ap) = delete; - bool parse(std::wstring_view codes, WriteCB write); + bool parse(std::wstring_view codes, ColorCB color_cb, WriteCB write_cb); }; diff --git a/ansi_parser.rl b/ansi_parser.rl index 21d7174..4327cbb 100644 --- a/ansi_parser.rl +++ b/ansi_parser.rl @@ -3,6 +3,7 @@ #include "dbc.hpp" #include #include "ansi_parser.hpp" +#include using namespace fmt; @@ -36,7 +37,9 @@ using namespace fmt; } } - action color24b { } + action color_out { + color_cb(color, bgcolor); + } action is_fg { target = color; } @@ -45,17 +48,30 @@ using namespace fmt; } action out { - write(bgcolor, color, fc); + write_cb(fc); } action reset_fg { color = $default_fg; } action reset_bg { bgcolor = $default_bg; } + action invert { + color = {100,100,100}; // UNDO THIS + bgcolor = $default_bg; // THIS TOO + color_cb(color, bgcolor); + } + action reset_invert { + color = $default_fg; + bgcolor = $default_bg; + color_cb(color, bgcolor); + } + action half_bright { + } action red { target.r = value; } action blue { target.g = value; } action green { target.b = value; } action start { value = 0; } - action end { } + action end {} + action log { println("command {}", (char)fc); } ESC = 0x1B; start = ESC "["; @@ -70,7 +86,16 @@ using namespace fmt; start %start ( reset | - (fg|bg) color24b num %red ";" num %blue ";" num %green %color24b + "0" | + "1" | + "2" %half_bright | + "3" | + "4" | + "5" | + "6" | + "7" %invert | + "27" %reset_invert | + (fg|bg) (color24b num %red ";" num %blue ";" num %green ) %color_out ) "m" %end ); @@ -93,7 +118,7 @@ ANSIParser::ANSIParser(sf::Color default_fg, sf::Color default_bg) : } } -bool ANSIParser::parse(std::wstring_view codes, WriteCB write) { +bool ANSIParser::parse(std::wstring_view codes, ColorCB color_cb, WriteCB write_cb) { const wchar_t *start = NULL; int cs = 0; unsigned int value = 0; @@ -107,5 +132,15 @@ bool ANSIParser::parse(std::wstring_view codes, WriteCB write) { %% write init; %% write exec; - return p - pe == 0; + bool good = p - pe == 0; + + if(!good) { + println("FAIL AT {}", p - pe); + // dear cthuhlu, save me from the pain that is wstring + for(int i = 0; i < 100; i++) { + print("{}", char(p[i])); + } + } + + return good; } diff --git a/gui.cpp b/gui.cpp index 0bdc2ad..56135b4 100644 --- a/gui.cpp +++ b/gui.cpp @@ -202,7 +202,7 @@ void GUI::shake() { int x = Random::uniform(-20,20); int y = Random::uniform(-20,20); // add x/y back to draw screen - $renderer.draw_screen($map_view, x, y); + $renderer.draw_grid($map_view, x, y); $renderer.display(); std::this_thread::sleep_for(1ms); } @@ -211,18 +211,8 @@ void GUI::shake() { void GUI::render_scene() { $renderer.clear(); - $renderer.draw_text_ui($status_ui, true); - $renderer.draw_screen($map_view); - - /* - Panel prompt(30, 10, GAME_MAP_POS + 30, 200); - prompt.set_renderer([&] { - return hbox({ - hflow(vbox(text("GOLD!"))) - }); - }); - $renderer.draw_text_ui(prompt, true); - */ + $renderer.draw_text($status_ui); + $renderer.draw_grid($map_view); $renderer.display(); } diff --git a/main.cpp b/main.cpp index 785c75d..922761a 100644 --- a/main.cpp +++ b/main.cpp @@ -9,6 +9,8 @@ #include "save.hpp" #include "ftxui/screen/terminal.hpp" // for SetColorSupport, Color, TrueColor #include +#include +#include using namespace ftxui; namespace fs = std::filesystem; @@ -49,6 +51,7 @@ void configure_world(DinkyECS::World &world, Map &game_map) { int main(int argc, char *argv[]) { + _setmode(_fileno(stdout), _O_U16TEXT); DinkyECS::World world; Map game_map(GAME_MAP_X, GAME_MAP_Y); diff --git a/render.cpp b/render.cpp index 62226b2..0ca52a0 100644 --- a/render.cpp +++ b/render.cpp @@ -4,6 +4,7 @@ #include #include #include "map.hpp" +#include using namespace fmt; @@ -104,45 +105,113 @@ inline void configure_tile(const sf::Sprite &sprite, sf::FloatRect &sp_bounds, height_delta = bg_bounds.height > sp_bounds.width ? (bg_bounds.height - sp_bounds.height) / 2 : 0; } -void SFMLRender::render_text(const std::wstring &text, float x, float y) { +void SFMLRender::render_grid(const std::wstring &text, float x, float y) { wchar_t last_tile = '#'; sf::FloatRect sp_bounds; float width_delta = 0; float height_delta = 0; sf::Sprite &sprite = get_text_sprite(last_tile); const float start_x = x; + sf::Color cur_fg = $default_fg; // make a copy so we don't modify the cached one - $ansi.parse(text, [&](sf::Color bg, sf::Color fg, wchar_t tile) { - if(tile == '\n') { - // don't bother processing newlines, just skip - y += $line_spacing; - x = start_x; - } else if(tile == L'\r') { - return; // skip these, just windows junk - } else { - $bg_sprite.setPosition({x, y}); + $ansi.parse(text, [&](sf::Color fg, sf::Color bg) { + cur_fg = fg; $bg_sprite.setColor(bg); - - // only get a new sprite if the tile changed - if(last_tile != tile) { - last_tile = tile; // update last tile seen - sprite = get_text_sprite(tile); - configure_tile(sprite, sp_bounds, $bg_bounds, width_delta, height_delta); + }, + + [&](wchar_t tile) { + if(tile == '\n') { + // don't bother processing newlines, just skip + y += $line_spacing; + x = start_x; + } else if(tile == L'\r') { + return; // skip these, just windows junk + } else { + $bg_sprite.setPosition({x, y}); + + // only get a new sprite if the tile changed + if(last_tile != tile) { + last_tile = tile; // update last tile seen + sprite = get_text_sprite(tile); + configure_tile(sprite, sp_bounds, $bg_bounds, width_delta, height_delta); + } + + sprite.setPosition({x+width_delta, y+height_delta}); + sprite.setColor(cur_fg); + + $window.draw($bg_sprite); + $window.draw(sprite); + // next cell + x += $base_glyph.advance; } + }); +} - sprite.setPosition({x+width_delta, y+height_delta}); - sprite.setColor(fg); +inline sf::FloatRect draw_chunk(sf::RenderWindow& window, sf::Text& text, float x, float y, std::wstring &out) { + text.setString(out); + text.setPosition(x, y); + sf::FloatRect bounds = text.getLocalBounds(); + // need left,top,width,height for box to be accurate + window.draw(text); + out.clear(); + return bounds; +} - $window.draw($bg_sprite); - $window.draw(sprite); - // next cell - x += $base_glyph.advance; +void SFMLRender::render_text(const std::wstring &text, float start_x, float start_y) { + std::wstring out; + float x = start_x; + float y = start_y; + enum State { START=1, BUILDING=2 }; + enum Event { NL=1, CHAR=3, SKIP=5 }; + State state = START; + Event event = CHAR; + + // start with the default_fg until it's changed + $ui_text.setFillColor($default_fg); + + $ansi.parse(text, + [&](sf::Color fg, sf::Color bg){ + if(out.size() > 0 ) { + auto bounds = draw_chunk($window, $ui_text, x, y, out); + x += bounds.width; + } + $ui_text.setFillColor(fg); + }, + [&](wchar_t tile) { + if(tile == '\r') event = SKIP; + else if(tile == '\n') event = NL; + else event = CHAR; + + switch(event) { + case SKIP: break; // ignore it + case NL: { + if(state == START) { + state = BUILDING; + } else if(state == BUILDING) { + sf::FloatRect bounds; + + if(out.size() > 0) { + bounds = draw_chunk($window, $ui_text, x, y, out); + } else { + bounds = $ui_text.getLocalBounds(); + } + + y += bounds.height; + x = start_x; // reset to the original position + } + } + break; + case CHAR: + state = BUILDING; + out += tile; + break; + } } - }); + ); } -void SFMLRender::draw_text_ui(Panel &panel, bool with_border) { +void SFMLRender::draw_text(Panel &panel, bool with_border) { sf::RectangleShape backing( sf::Vector2f($ui_bounds.width * panel.width, $ui_bounds.height * panel.height)); @@ -159,13 +228,11 @@ void SFMLRender::draw_text_ui(Panel &panel, bool with_border) { panel.render(); const std::wstring &panelout = panel.to_string(); - $ui_text.setPosition(panel.x, panel.y); - $ui_text.setString(panelout); - $window.draw($ui_text); + render_text(panelout, panel.x, panel.y); } -void SFMLRender::draw_screen(Panel &panel, float x, float y) { +void SFMLRender::draw_grid(Panel &panel, float x, float y) { panel.render(); const std::wstring &panelout = panel.to_string(); - render_text(panelout, panel.x + x, panel.y + y); + render_grid(panelout, panel.x + x, panel.y + y); } diff --git a/render.hpp b/render.hpp index 453c182..d0f6f21 100644 --- a/render.hpp +++ b/render.hpp @@ -55,9 +55,10 @@ struct SFMLRender { sf::Color color(Value val); sf::Sprite &get_text_sprite(wchar_t tile); bool resize_map(int new_size, Point &view_port); + void render_grid(const std::wstring &text, float x, float y); void render_text(const std::wstring &text, float x, float y); - void draw_text_ui(Panel &panel, bool with_border=false); - void draw_screen(Panel &panel, float map_off_x=0.0f, float map_off_y=0.0f); + void draw_text(Panel &panel, bool with_border=false); + void draw_grid(Panel &panel, float map_off_x=0.0f, float map_off_y=0.0f); bool poll_event(sf::Event &event) { return $window.pollEvent(event); diff --git a/status.txt b/status.txt index e609b5a..02286b6 100644 --- a/status.txt +++ b/status.txt @@ -1,4 +1,6 @@ -NOTES: +TODAY'S GOAL: + +* Run the ansi_parser on the whole UI so I can use colors and other glyphs. TODO: @@ -6,7 +8,6 @@ TODO: * Can std::any be defaulted to a noop in the events? * Save file isn't saving gold. * Inventory needs to be better, but need some kinds of "weapons" or other loot to get and not just gold. -* Run the ansi_parser on the whole UI so I can use colors and other glyphs. * Create a few more enemy types to fight. * Devise a more complete map/world generator that can use the loot and enemies better. * Maybe an LOS system, but the hearing version works pretty well so far. diff --git a/tests/ansi_parser.cpp b/tests/ansi_parser.cpp index 7cf84e5..509eb6e 100644 --- a/tests/ansi_parser.cpp +++ b/tests/ansi_parser.cpp @@ -63,7 +63,11 @@ TEST_CASE("test out ragel parser", "[gui]") { std::cout << colors; - bool good = ansi.parse(colors_utf, [&](sf::Color bgcolor, sf::Color color, wchar_t ch) { + bool good = ansi.parse(colors_utf, + [&](sf::Color color, sf::Color bgcolor){ + // ignore color + }, + [&](wchar_t ch) { bool correct_char = ch == '#' || ch == ' ' || ch == '\n' || ch == '\r'; REQUIRE(correct_char); });