screen.h 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375
  1. #ifndef __MAUDIT_SCREEN_H
  2. #define __MAUDIT_SCREEN_H
  3. #include <ctype.h>
  4. #include <stdlib.h>
  5. #include <functional>
  6. namespace maudit {
  7. #define CSI "\033["
  8. template <typename IO>
  9. struct screen {
  10. IO& io;
  11. unsigned int w;
  12. unsigned int h;
  13. bool is_cr;
  14. unsigned int address;
  15. std::function<void (screen<IO>*, std::vector<glyph>&)> callback;
  16. screen(IO& _io) : io(_io), w(0), h(0), is_cr(false), address(io.peer_ip()) {
  17. // Turn off echo in the client.
  18. io.write("\xFF\xFB\x01");
  19. // Turn off line buffering in the client.
  20. io.write("\xFF\xFB\x03");
  21. // Turn on compression.
  22. io.write("\xFF\xFB\x55");
  23. }
  24. void reset_color() {
  25. io.write(CSI "0m");
  26. }
  27. void clear() {
  28. io.write(CSI "H" CSI "J");
  29. }
  30. template <typename FUNC>
  31. bool refresh(const FUNC& f) {
  32. if (w == 0 || h == 0) {
  33. keypress tmp;
  34. // Request terminal size.
  35. //io.write(CSI "18t");
  36. io.write("\xFF\xFD\x1F");
  37. if (!wait_key(tmp)) {
  38. throw std::runtime_error("Could not detect terminal size.");
  39. }
  40. //w = tmp.w;
  41. //h = tmp.h;
  42. }
  43. std::vector<glyph> screen;
  44. screen.resize(w*h);
  45. f(screen, w, h);
  46. bool ok = send_screen(screen);
  47. if (!ok)
  48. return false;
  49. if (callback) {
  50. callback(this, screen);
  51. }
  52. return true;
  53. }
  54. bool send_screen(const std::vector<glyph>& glyphs) {
  55. std::string data;
  56. data += CSI "H"; // Move cursor to home position.
  57. //data += CSI "2J"; // Erase screen.
  58. color fore_prev = color::none;
  59. color back_prev = color::none;
  60. bool ul_prev = false;
  61. bool first = true;
  62. for (size_t y = 0; y < h; ++y) {
  63. for (size_t x = 0; x < w; ++x) {
  64. const glyph& g = glyphs[y*w+x];
  65. const char* gtext = g.get_text();
  66. if (gtext[0] == '\0')
  67. continue;
  68. bool do_fore = false;
  69. bool do_back = false;
  70. if (first) {
  71. data += CSI;
  72. data += "0m";
  73. do_fore = true;
  74. do_back = true;
  75. first = false;
  76. } else {
  77. if (fore_prev != g.fore || ul_prev != g.underline) {
  78. do_fore = true;
  79. if ((fore_prev >= color::bright_black && g.fore < color::bright_black) ||
  80. (ul_prev != g.underline)) {
  81. data += CSI;
  82. data += "0m";
  83. do_fore = true;
  84. do_back = true;
  85. }
  86. }
  87. if (back_prev != g.back) {
  88. do_back = true;
  89. }
  90. }
  91. if (do_fore || do_back) {
  92. data += CSI;
  93. if (do_fore) {
  94. if (g.underline) {
  95. data += "4;";
  96. }
  97. switch (g.fore) {
  98. case color::none: data += "39"; break;
  99. case color::dim_black: data += "30"; break;
  100. case color::dim_red: data += "31"; break;
  101. case color::dim_green: data += "32"; break;
  102. case color::dim_yellow: data += "33"; break;
  103. case color::dim_blue: data += "34"; break;
  104. case color::dim_magenta: data += "35"; break;
  105. case color::dim_cyan: data += "36"; break;
  106. case color::dim_white: data += "37"; break;
  107. case color::bright_black: data += "1;30"; break;
  108. case color::bright_red: data += "1;31"; break;
  109. case color::bright_green: data += "1;32"; break;
  110. case color::bright_yellow: data += "1;33"; break;
  111. case color::bright_blue: data += "1;34"; break;
  112. case color::bright_magenta: data += "1;35"; break;
  113. case color::bright_cyan: data += "1;36"; break;
  114. case color::bright_white: data += "1;37"; break;
  115. }
  116. }
  117. if (do_back) {
  118. if (do_fore) data += ";";
  119. switch (g.back) {
  120. case color::none: data += "49"; break;
  121. case color::dim_black: data += "40"; break;
  122. case color::dim_red: data += "41"; break;
  123. case color::dim_green: data += "42"; break;
  124. case color::dim_yellow: data += "43"; break;
  125. case color::dim_blue: data += "44"; break;
  126. case color::dim_magenta: data += "45"; break;
  127. case color::dim_cyan: data += "46"; break;
  128. case color::dim_white: data += "47"; break;
  129. case color::bright_black: data += "40"; break;
  130. case color::bright_red: data += "41"; break;
  131. case color::bright_green: data += "42"; break;
  132. case color::bright_yellow: data += "43"; break;
  133. case color::bright_blue: data += "44"; break;
  134. case color::bright_magenta: data += "45"; break;
  135. case color::bright_cyan: data += "46"; break;
  136. case color::bright_white: data += "47"; break;
  137. }
  138. }
  139. data += 'm';
  140. }
  141. data += gtext;
  142. fore_prev = g.fore;
  143. back_prev = g.back;
  144. ul_prev = g.underline;
  145. }
  146. }
  147. return io.write(data);
  148. }
  149. bool wait_key(keypress& out) {
  150. out.w = w;
  151. out.h = h;
  152. bool prev_is_cr = is_cr;
  153. again:
  154. is_cr = false;
  155. unsigned char c;
  156. bool ok = io.read(c);
  157. if (!ok) return false;
  158. // Handle telnet escape codes.
  159. if (c == 0xFF) {
  160. ok = io.read(c);
  161. if (!ok) return false;
  162. switch (c) {
  163. case 0xFF:
  164. // Escape 0xFF
  165. out.letter = 0xFF;
  166. return true;
  167. case 0xFB:
  168. case 0xFC:
  169. case 0xFE:
  170. // Will/won't/don't silliness, ignore it.
  171. ok = io.read(c);
  172. if (!ok) return false;
  173. goto again;
  174. case 0xFD:
  175. // 'Do' silliness.
  176. ok = io.read(c);
  177. if (!ok) return false;
  178. if (c == 0x55) {
  179. // Enable compression.
  180. io.write("\xFF\xFA\x55\xFF\xF0");
  181. io.set_compression(true);
  182. }
  183. goto again;
  184. case 0xFA:
  185. ok = io.read(c);
  186. if (!ok) return false;
  187. if (c != 0x1F)
  188. goto again;
  189. // 0xFA 0x1F: response to screen size request.
  190. ok = io.read(c);
  191. if (!ok) return false;
  192. out.w = ((size_t)c << 8);
  193. ok = io.read(c);
  194. if (!ok) return false;
  195. out.w |= (size_t)c;
  196. ok = io.read(c);
  197. if (!ok) return false;
  198. out.h = ((size_t)c << 8);
  199. ok = io.read(c);
  200. if (!ok) return false;
  201. out.h |= (size_t)c;
  202. out.key = keycode::resize;
  203. w = out.w;
  204. h = out.h;
  205. return true;
  206. goto again;
  207. default:
  208. goto again;
  209. }
  210. }
  211. // Handle ANSI/vt100 terminal escapes.
  212. if (c == '\033') {
  213. ok = io.read(c);
  214. if (!ok) return false;
  215. if (c == '[') {
  216. // Try for some special characters.
  217. ok = io.read(c);
  218. if (!ok) return false;
  219. switch (c) {
  220. case 'D': out.key = keycode::left; break;
  221. case 'A': out.key = keycode::up; break;
  222. case 'C': out.key = keycode::right; break;
  223. case 'B': out.key = keycode::down; break;
  224. case '1': out.key = keycode::kp_7; break;
  225. case '5': out.key = keycode::kp_9; break;
  226. case '4': out.key = keycode::kp_1; break;
  227. case '6': out.key = keycode::kp_3; break;
  228. case '3': out.key = keycode::del; break;
  229. }
  230. if (out.key == keycode::kp_7 ||
  231. out.key == keycode::kp_9 ||
  232. out.key == keycode::kp_1 ||
  233. out.key == keycode::kp_3 ||
  234. out.key == keycode::del) {
  235. ok = io.read(c);
  236. if (!ok) return false;
  237. }
  238. } else {
  239. out.key = keycode::esc;
  240. }
  241. return true;
  242. }
  243. // Handle CR/LF insane madness.
  244. /*
  245. \r -> \n
  246. \r\n -> \n
  247. \r\0 -> \n
  248. */
  249. if (prev_is_cr && (c == '\n' || c == '\0')) {
  250. goto again;
  251. }
  252. if (c == '\r') {
  253. is_cr = true;
  254. out.letter = '\n';
  255. return true;
  256. }
  257. out.letter = c;
  258. return true;
  259. }
  260. };
  261. }
  262. #endif