terminal.cpp 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401
  1. /*
  2. Terminal tracking
  3. Actually, I believe I only really need to track the color information.
  4. Everything else, I'm not sure I really care about. (NNY!)
  5. */
  6. #include <ctype.h>
  7. #include <stdio.h> // snprintf
  8. #include <stdlib.h>
  9. #include <string.h>
  10. #include "terminal.h"
  11. #include "utils.h"
  12. #include "zf_log.h"
  13. void console_init(struct console_details *cdp) {
  14. cdp->posx = 0;
  15. cdp->posy = 0;
  16. cdp->savedx = 0;
  17. cdp->savedy = 0;
  18. cdp->in_ansi = 0;
  19. cdp->fgcolor = 7;
  20. cdp->bgcolor = 0;
  21. cdp->status = 0;
  22. }
  23. void ansi_color(struct console_details *cdp, int color) {
  24. // ZF_LOGV("ansi_color(%d)", color);
  25. if (color == 0) {
  26. cdp->status = 0;
  27. cdp->fgcolor = 7;
  28. cdp->bgcolor = 0;
  29. return;
  30. }
  31. if (color == 1) {
  32. cdp->status = 1;
  33. return;
  34. }
  35. if (color == 5) {
  36. cdp->status = 5;
  37. return;
  38. }
  39. if ((color >= 30) && (color <= 37)) {
  40. cdp->fgcolor = color - 30;
  41. return;
  42. }
  43. if ((color >= 40) && (color <= 47)) {
  44. cdp->bgcolor = color - 40;
  45. return;
  46. }
  47. ZF_LOGD("ansi_color( %d ) is unknown to me.", color);
  48. }
  49. const char *color_restore(struct console_details *cdp) {
  50. static char buffer[30];
  51. int slen;
  52. if (cdp->status == 0) {
  53. slen = snprintf(buffer, sizeof(buffer), "\x1b[0;3%d;4%dm", cdp->fgcolor,
  54. cdp->bgcolor);
  55. if (slen >= sizeof(buffer)) {
  56. ZF_LOGE("snprintf %d > size %d", slen, (int)sizeof(buffer));
  57. buffer[0] = 0;
  58. }
  59. } else {
  60. slen = snprintf(buffer, sizeof(buffer), "\x1b[0;%d;3%d;4%dm", cdp->status,
  61. cdp->fgcolor, cdp->bgcolor);
  62. if (slen >= sizeof(buffer)) {
  63. ZF_LOGE("snprintf %d > size %d", slen, (int)sizeof(buffer));
  64. buffer[0] = 0;
  65. }
  66. };
  67. return buffer;
  68. }
  69. ANSI_TYPE console_ansi(struct console_details *cdp, const char *ansi) {
  70. int understood = 0;
  71. const char *cp = ansi;
  72. const char *last = ansi + strlen(ansi) - 1;
  73. int number, number2;
  74. if (*cp == '\x1b') {
  75. cp++;
  76. // Ok, that's expected.
  77. if (*cp == '[') {
  78. cp++;
  79. // https://en.wikipedia.org/wiki/ANSI_escape_code#CSI_sequences
  80. switch (*last) {
  81. case 'A':
  82. // cursor up
  83. if (cp == last) {
  84. number = 1;
  85. } else {
  86. number = atoi(cp);
  87. if (number < 1) {
  88. ZF_LOGD("console_ansi( %s ): number error (%d)", repr(ansi),
  89. number);
  90. number = 1;
  91. }
  92. };
  93. cdp->posy -= number;
  94. if (cdp->posy < 0) {
  95. cdp->posy = 0;
  96. ZF_LOGD(
  97. "console_ansi( %s ): attempt to move above top of screen (%d)",
  98. repr(ansi), number);
  99. }
  100. understood = 1;
  101. return CURSOR;
  102. case 'B':
  103. // cursor down
  104. if (cp == last) {
  105. number = 1;
  106. } else {
  107. number = atoi(cp);
  108. if (number < 1) {
  109. ZF_LOGD("console_ansi( %s ): number error (%d)", repr(ansi),
  110. number);
  111. number = 1;
  112. }
  113. };
  114. cdp->posy += number;
  115. // check range/"scroll"
  116. understood = 1;
  117. return CURSOR;
  118. case 'C':
  119. // cursor forward
  120. if (cp == last) {
  121. number = 1;
  122. } else {
  123. number = atoi(cp);
  124. if (number < 1) {
  125. ZF_LOGD("console_ansi( %s ): number error (%d)", repr(ansi),
  126. number);
  127. number = 1;
  128. }
  129. };
  130. cdp->posx += number;
  131. // Well. According to the "spec", the screen limits are hard
  132. // If the cursor is already at the edge of the screen, this has no
  133. // effect. ^ This is *NOT* how any ANSI BBS terminal program acts!
  134. while (cdp->posx > 79) {
  135. cdp->posy++;
  136. // check range/"scroll"
  137. cdp->posx -= 79;
  138. }
  139. understood = 1;
  140. return CURSOR;
  141. case 'D':
  142. // cursor backwards
  143. if (cp == last) {
  144. number = 1;
  145. } else {
  146. number = atoi(cp);
  147. if (number < 1) {
  148. ZF_LOGD("console_ansi( %s ): number error (%d)", repr(ansi),
  149. number);
  150. number = 0;
  151. }
  152. };
  153. cdp->posx -= number;
  154. // Well. According to the "spec", the screen limits are hard
  155. // If the cursor is already at the edge of the screen, this has no
  156. // effect. ^ This is *NOT* how any ANSI BBS terminal program acts!
  157. while (cdp->posx < 0) {
  158. cdp->posy--;
  159. if (cdp->posy < 0) {
  160. cdp->posy = 0;
  161. }
  162. cdp->posx += 79;
  163. }
  164. understood = 1;
  165. return CURSOR;
  166. case 'H':
  167. // cursor position
  168. if (*cp == ';') {
  169. // Missing first number
  170. number = 1;
  171. cp++;
  172. if (cp == last) {
  173. // missing 2nd number as well?
  174. number2 = 1;
  175. } else {
  176. number2 = atoi(cp);
  177. }
  178. } else {
  179. // Ok, find the first number
  180. number = atoi(cp);
  181. cp = strchr(cp, ';');
  182. if (cp == NULL) {
  183. // Missing 2nd number
  184. number2 = 1;
  185. } else {
  186. // 2nd number found, maybe.
  187. cp++;
  188. if (cp == last) {
  189. number2 = 1;
  190. } else {
  191. number2 = atoi(cp);
  192. }
  193. }
  194. }
  195. // Our positions start at zero, not one.
  196. cdp->posx = number - 1;
  197. cdp->posy = number2 - 1;
  198. understood = 1;
  199. return CURSOR;
  200. case 'J':
  201. // clear
  202. if (cp == last) {
  203. number = 0;
  204. } else {
  205. number = atoi(cp);
  206. };
  207. // clears ... part of the screen.
  208. if (number == 2) {
  209. cdp->posx = 0;
  210. cdp->posy = 0;
  211. };
  212. understood = 1;
  213. return CLEAR;
  214. case 'm':
  215. // color
  216. if (cp == last) {
  217. // nothing given, default to 0.
  218. number = 0;
  219. ansi_color(cdp, number);
  220. } else {
  221. while (cp != last) {
  222. number = atoi(cp);
  223. ansi_color(cdp, number);
  224. cp++;
  225. while ((cp != last) && (isdigit(*cp))) {
  226. cp++;
  227. };
  228. if (cp != last) {
  229. if (*cp == ';') {
  230. cp++;
  231. }
  232. }
  233. }
  234. }
  235. understood = 1;
  236. return COLOR;
  237. case 't':
  238. case 'r':
  239. case 'h':
  240. case '!':
  241. // These are ones that I don't care about.
  242. case 'n': // This is terminal detect -- give me cursor position
  243. understood = 1;
  244. return OTHER;
  245. default:
  246. // unsure -- possibly not important
  247. ZF_LOGD("console_ansi( %s ): ???", repr(ansi));
  248. understood = 0;
  249. return OTHER;
  250. }
  251. }
  252. }
  253. }
  254. /*
  255. * console_char()
  256. * return whether or not we are still in_ansi
  257. */
  258. termchar console_char(struct console_details *cdp, char ch) {
  259. char *cp;
  260. termchar tc;
  261. if (cdp->in_ansi) {
  262. // Ok, append this char
  263. cp = cdp->ansi + strlen(cdp->ansi);
  264. *cp = ch;
  265. cp++;
  266. *cp = 0;
  267. if (isalpha(ch)) {
  268. // Ok!
  269. tc.ansi = console_ansi(cdp, cdp->ansi);
  270. cdp->in_ansi = 0;
  271. cdp->ansi[0] = 0;
  272. tc.in_ansi = 1;
  273. return tc;
  274. }
  275. tc.ansi = START;
  276. tc.in_ansi = 1;
  277. return tc;
  278. } else {
  279. tc.ansi = START;
  280. tc.in_ansi = 0;
  281. if (ch == '\x1b') {
  282. cp = cdp->ansi;
  283. *cp = ch;
  284. cp++;
  285. *cp = 0;
  286. cdp->in_ansi = 1;
  287. tc.in_ansi = 1;
  288. return tc;
  289. }
  290. // should I try reporting MOTION non-ANSI ?
  291. if (ch == '\r') {
  292. // Carriage return
  293. cdp->posx = 0;
  294. return tc;
  295. }
  296. if (ch == '\n') {
  297. cdp->posy++;
  298. // check range/"scroll"
  299. return tc;
  300. }
  301. if (ch == '\b') {
  302. // Backspace.
  303. if (cdp->posx > 0) {
  304. cdp->posx--;
  305. }
  306. return tc;
  307. }
  308. if (ch == '\f') {
  309. // form feed
  310. // treat as clear screen
  311. cdp->posx = 0;
  312. cdp->posy = 0;
  313. return tc;
  314. }
  315. /*
  316. I don't believe that anything else can possibly be here, other then an
  317. actual printable character. So!
  318. */
  319. cdp->posx++;
  320. if (cdp->posx > 79) {
  321. cdp->posx = 0;
  322. cdp->posy++;
  323. // check range/"scroll"
  324. }
  325. return tc;
  326. }
  327. }
  328. #ifdef UNWANTED
  329. void console_string(struct console_details *cdp, const char *chars) {
  330. int x;
  331. for (x = 0; x < strlen(chars); x++) {
  332. console_char(cdp, chars[x]);
  333. }
  334. }
  335. #endif
  336. // extern void log_flush(void);
  337. void console_receive(struct console_details *cdp, std::string chars) {
  338. ZF_LOGI("console_char %lu chars", chars.size());
  339. /* // the c way
  340. for (int x = 0; x < chars.size(); x++) {
  341. console_char(cdp, chars[x]);
  342. }
  343. */
  344. // the C++ way
  345. for (auto strit = chars.begin(); strit != chars.end(); strit++)
  346. console_char(cdp, *strit);
  347. }
  348. #ifdef UNWANTED
  349. void console_receive(struct console_details *cdp, const char *chars, int len) {
  350. int x;
  351. for (x = 0; x < len; x++) {
  352. console_char(cdp, chars[x]);
  353. }
  354. }
  355. #endif