123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375 |
- #ifndef __MAUDIT_SCREEN_H
- #define __MAUDIT_SCREEN_H
- #include <ctype.h>
- #include <stdlib.h>
- #include <functional>
- namespace maudit {
- #define CSI "\033["
- template <typename IO>
- struct screen {
-
- IO& io;
- unsigned int w;
- unsigned int h;
- bool is_cr;
- unsigned int address;
- std::function<void (screen<IO>*, std::vector<glyph>&)> callback;
- screen(IO& _io) : io(_io), w(0), h(0), is_cr(false), address(io.peer_ip()) {
- // Turn off echo in the client.
- io.write("\xFF\xFB\x01");
- // Turn off line buffering in the client.
- io.write("\xFF\xFB\x03");
- // Turn on compression.
- io.write("\xFF\xFB\x55");
- }
- void reset_color() {
- io.write(CSI "0m");
- }
- void clear() {
- io.write(CSI "H" CSI "J");
- }
- template <typename FUNC>
- bool refresh(const FUNC& f) {
- if (w == 0 || h == 0) {
- keypress tmp;
- // Request terminal size.
- //io.write(CSI "18t");
- io.write("\xFF\xFD\x1F");
- if (!wait_key(tmp)) {
- throw std::runtime_error("Could not detect terminal size.");
- }
- //w = tmp.w;
- //h = tmp.h;
- }
- std::vector<glyph> screen;
- screen.resize(w*h);
- f(screen, w, h);
- bool ok = send_screen(screen);
- if (!ok)
- return false;
- if (callback) {
- callback(this, screen);
- }
- return true;
- }
- bool send_screen(const std::vector<glyph>& glyphs) {
- std::string data;
- data += CSI "H"; // Move cursor to home position.
- //data += CSI "2J"; // Erase screen.
- color fore_prev = color::none;
- color back_prev = color::none;
- bool ul_prev = false;
- bool first = true;
- for (size_t y = 0; y < h; ++y) {
- for (size_t x = 0; x < w; ++x) {
- const glyph& g = glyphs[y*w+x];
- const char* gtext = g.get_text();
- if (gtext[0] == '\0')
- continue;
- bool do_fore = false;
- bool do_back = false;
- if (first) {
- data += CSI;
- data += "0m";
- do_fore = true;
- do_back = true;
- first = false;
- } else {
- if (fore_prev != g.fore || ul_prev != g.underline) {
- do_fore = true;
- if ((fore_prev >= color::bright_black && g.fore < color::bright_black) ||
- (ul_prev != g.underline)) {
- data += CSI;
- data += "0m";
- do_fore = true;
- do_back = true;
- }
- }
-
- if (back_prev != g.back) {
- do_back = true;
- }
- }
-
- if (do_fore || do_back) {
- data += CSI;
- if (do_fore) {
- if (g.underline) {
- data += "4;";
- }
- switch (g.fore) {
- case color::none: data += "39"; break;
- case color::dim_black: data += "30"; break;
- case color::dim_red: data += "31"; break;
- case color::dim_green: data += "32"; break;
- case color::dim_yellow: data += "33"; break;
- case color::dim_blue: data += "34"; break;
- case color::dim_magenta: data += "35"; break;
- case color::dim_cyan: data += "36"; break;
- case color::dim_white: data += "37"; break;
- case color::bright_black: data += "1;30"; break;
- case color::bright_red: data += "1;31"; break;
- case color::bright_green: data += "1;32"; break;
- case color::bright_yellow: data += "1;33"; break;
- case color::bright_blue: data += "1;34"; break;
- case color::bright_magenta: data += "1;35"; break;
- case color::bright_cyan: data += "1;36"; break;
- case color::bright_white: data += "1;37"; break;
- }
- }
- if (do_back) {
- if (do_fore) data += ";";
- switch (g.back) {
- case color::none: data += "49"; break;
- case color::dim_black: data += "40"; break;
- case color::dim_red: data += "41"; break;
- case color::dim_green: data += "42"; break;
- case color::dim_yellow: data += "43"; break;
- case color::dim_blue: data += "44"; break;
- case color::dim_magenta: data += "45"; break;
- case color::dim_cyan: data += "46"; break;
- case color::dim_white: data += "47"; break;
- case color::bright_black: data += "40"; break;
- case color::bright_red: data += "41"; break;
- case color::bright_green: data += "42"; break;
- case color::bright_yellow: data += "43"; break;
- case color::bright_blue: data += "44"; break;
- case color::bright_magenta: data += "45"; break;
- case color::bright_cyan: data += "46"; break;
- case color::bright_white: data += "47"; break;
- }
- }
-
- data += 'm';
- }
- data += gtext;
- fore_prev = g.fore;
- back_prev = g.back;
- ul_prev = g.underline;
- }
- }
- return io.write(data);
- }
- bool wait_key(keypress& out) {
- out.w = w;
- out.h = h;
- bool prev_is_cr = is_cr;
- again:
- is_cr = false;
- unsigned char c;
- bool ok = io.read(c);
- if (!ok) return false;
- // Handle telnet escape codes.
- if (c == 0xFF) {
- ok = io.read(c);
- if (!ok) return false;
- switch (c) {
- case 0xFF:
- // Escape 0xFF
- out.letter = 0xFF;
- return true;
- case 0xFB:
- case 0xFC:
- case 0xFE:
- // Will/won't/don't silliness, ignore it.
- ok = io.read(c);
- if (!ok) return false;
- goto again;
- case 0xFD:
- // 'Do' silliness.
- ok = io.read(c);
- if (!ok) return false;
- if (c == 0x55) {
- // Enable compression.
- io.write("\xFF\xFA\x55\xFF\xF0");
- io.set_compression(true);
- }
- goto again;
- case 0xFA:
- ok = io.read(c);
- if (!ok) return false;
- if (c != 0x1F)
- goto again;
- // 0xFA 0x1F: response to screen size request.
- ok = io.read(c);
- if (!ok) return false;
- out.w = ((size_t)c << 8);
- ok = io.read(c);
- if (!ok) return false;
- out.w |= (size_t)c;
-
- ok = io.read(c);
- if (!ok) return false;
- out.h = ((size_t)c << 8);
- ok = io.read(c);
- if (!ok) return false;
- out.h |= (size_t)c;
-
- out.key = keycode::resize;
- w = out.w;
- h = out.h;
- return true;
- goto again;
- default:
- goto again;
- }
- }
- // Handle ANSI/vt100 terminal escapes.
- if (c == '\033') {
- ok = io.read(c);
- if (!ok) return false;
- if (c == '[') {
- // Try for some special characters.
- ok = io.read(c);
- if (!ok) return false;
- switch (c) {
- case 'D': out.key = keycode::left; break;
- case 'A': out.key = keycode::up; break;
- case 'C': out.key = keycode::right; break;
- case 'B': out.key = keycode::down; break;
-
- case '1': out.key = keycode::kp_7; break;
- case '5': out.key = keycode::kp_9; break;
- case '4': out.key = keycode::kp_1; break;
- case '6': out.key = keycode::kp_3; break;
- case '3': out.key = keycode::del; break;
- }
- if (out.key == keycode::kp_7 ||
- out.key == keycode::kp_9 ||
- out.key == keycode::kp_1 ||
- out.key == keycode::kp_3 ||
- out.key == keycode::del) {
- ok = io.read(c);
- if (!ok) return false;
- }
- } else {
- out.key = keycode::esc;
- }
- return true;
- }
- // Handle CR/LF insane madness.
- /*
- \r -> \n
- \r\n -> \n
- \r\0 -> \n
- */
- if (prev_is_cr && (c == '\n' || c == '\0')) {
- goto again;
- }
- if (c == '\r') {
- is_cr = true;
- out.letter = '\n';
- return true;
- }
- out.letter = c;
- return true;
- }
- };
- }
- #endif
|