input.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405
  1. #include "input.h"
  2. #include "render.h"
  3. bool allow_part = false;
  4. bool allow_join = false;
  5. bool has_quit = false;
  6. int ms_input_delay = 250;
  7. std::string input;
  8. std::string prompt; // mostly for length to erase/restore properly
  9. int max_input = 220;
  10. int input_scroll = 0;
  11. door::ANSIColor prompt_color{door::COLOR::YELLOW, door::COLOR::BLUE,
  12. door::ATTR::BOLD};
  13. door::ANSIColor input_color{door::COLOR::WHITE}; // , door::COLOR::BLUE};
  14. void erase(door::Door &d, int count) {
  15. d << door::reset;
  16. for (int x = 0; x < count; ++x) {
  17. d << "\x08 \x08";
  18. }
  19. }
  20. void clear_input(door::Door &d) {
  21. if (prompt.empty())
  22. return;
  23. if (input_scroll == 0)
  24. erase(d, input.size());
  25. else
  26. erase(d, input.size() - input_scroll + 3);
  27. erase(d, prompt.size() + 1);
  28. }
  29. void restore_input(door::Door &d) {
  30. if (prompt.empty())
  31. return;
  32. d << prompt_color << prompt << input_color << " ";
  33. if (input_scroll == 0)
  34. d << input;
  35. else
  36. d << "..." << input.substr(input_scroll);
  37. }
  38. /*
  39. commands:
  40. /h /help /?
  41. /t /talk /talkto [TARGET]
  42. /msg [TO] [message]
  43. /me [message]
  44. /quit [message, maybe]
  45. /join [TARGET]
  46. /part [TARGET]
  47. future:
  48. /list ?
  49. /version ?
  50. */
  51. void parse_input(door::Door &door, ircClient &irc) {
  52. // yes, we have something
  53. std::time_t now_t;
  54. time(&now_t);
  55. if (input[0] == '/') {
  56. // command given
  57. std::vector<std::string> cmd = split_limit(input, 3);
  58. if (cmd[0] == "/motd") {
  59. irc.write("MOTD");
  60. }
  61. if (cmd[0] == "/names") {
  62. std::string talk = irc.talkto();
  63. if (talk[0] == '#') {
  64. // or we could pull this from /info & talk.
  65. irc.write("NAMES " + talk);
  66. } // handle this input error
  67. }
  68. if (cmd[0] == "/quit") {
  69. irc.write("QUIT");
  70. }
  71. if (cmd[0] == "/talkto") {
  72. irc.talkto(cmd[1]);
  73. door << "[talkto = " << cmd[1] << "]" << door::nl;
  74. }
  75. if (cmd[0] == "/join") {
  76. if (allow_join) {
  77. std::string tmp = "JOIN " + cmd[1];
  78. irc.write(tmp);
  79. }
  80. }
  81. if (cmd[0] == "/part") {
  82. if (allow_part) {
  83. std::string tmp = "PART " + cmd[1];
  84. irc.write(tmp);
  85. }
  86. }
  87. // TODO: feed this and /me to render so DRY/one place to render
  88. if (cmd[0] == "/msg") {
  89. std::string tmp = "PRIVMSG " + cmd[1] + " :" + cmd[2];
  90. irc.write(tmp);
  91. // build msg for render
  92. tmp = ":" + irc.nick + "!" + " " + tmp;
  93. message_stamp msg;
  94. msg.buffer = irc_split(tmp);
  95. render(msg, door, irc);
  96. /*
  97. stamp(now_t, door);
  98. if (cmd[1][0] == '#') {
  99. door << irc.nick << "/" << cmd[1] << " " << cmd[2] << door::nl;
  100. } else {
  101. door << cmd[1] << " " << cmd[2] << door::nl;
  102. }
  103. */
  104. }
  105. if (cmd[0] == "/notice") {
  106. std::string tmp = "NOTICE " + cmd[1] + " :" + cmd[2];
  107. irc.write(tmp);
  108. // build msg for render
  109. tmp = ":" + irc.nick + "!" + " " + tmp;
  110. message_stamp msg;
  111. msg.buffer = irc_split(tmp);
  112. render(msg, door, irc);
  113. }
  114. if (cmd[0] == "/me") {
  115. cmd = split_limit(input, 2);
  116. std::string tmp =
  117. "PRIVMSG " + irc.talkto() + " :\x01" + "ACTION " + cmd[1] + "\x01";
  118. irc.write(tmp);
  119. // build msg for render
  120. tmp = ":" + irc.nick + "!" + " ACTION " + irc.talkto() + " :" + cmd[1];
  121. message_stamp msg;
  122. msg.buffer = irc_split(tmp);
  123. render(msg, door, irc);
  124. /*
  125. stamp(now_t, door);
  126. door << "* " << irc.nick << " " << cmd[1] << door::nl;
  127. */
  128. }
  129. if (cmd[0] == "/nick") {
  130. std::string tmp = "NICK " + cmd[1];
  131. irc.write(tmp);
  132. }
  133. if (cmd[0] == "/help") {
  134. door << "IRC Commands :" << door::nl;
  135. door << "/help /motd /quit /nick" << door::nl;
  136. door << "/me ACTION" << door::nl;
  137. door << "/msg TARGET Message" << door::nl;
  138. if (allow_part) {
  139. door << "/join #CHANNEL" << door::nl;
  140. door << "/part #CHANNEL" << door::nl;
  141. }
  142. door << "[ESC] aborts input" << door::nl;
  143. }
  144. if (cmd[0] == "/flood") {
  145. std::string target = irc.talkto();
  146. std::string bugz = "bugz";
  147. for (int x = 0; x < 20; ++x) {
  148. std::string message = "PRIVMSG " + target +
  149. " : CHANNEL FLOOD TESTING THIS IS MESSAGE " +
  150. std::to_string(x + 1) + " TEST TEST TEST";
  151. irc.write_queue(target, message);
  152. message = "PRIVMSG " + bugz + " : USER FLOOD TESTING THIS IS MESSAGE " +
  153. std::to_string(x + 1) + " TEST TEST TEST";
  154. irc.write_queue(bugz, message);
  155. message = "PRIVMSG apollo : USER FLOOD TESTING THIS IS MESSAGE " +
  156. std::to_string(x + 1) + " TEST TEST TEST";
  157. irc.write_queue("apollo", message);
  158. }
  159. }
  160. if (cmd[0] == "/info") {
  161. irc.channels_lock.lock();
  162. for (auto c : irc.channels) {
  163. door << "CH " << c.first << " ";
  164. for (auto s : c.second) {
  165. door << s << " ";
  166. }
  167. door << door::nl;
  168. }
  169. irc.channels_lock.unlock();
  170. }
  171. } else {
  172. std::string target = irc.talkto();
  173. std::string output = "PRIVMSG " + target + " :" + input;
  174. // I need to display something here to show we've said something (and
  175. // where we've said it)
  176. door::ANSIColor nick_color{door::COLOR::WHITE, door::COLOR::BLUE};
  177. irc.write(output);
  178. // build message for render
  179. message_stamp msg;
  180. output = ":" + irc.nick + "!" + " " + output;
  181. msg.buffer = irc_split(output);
  182. render(msg, door, irc);
  183. /*
  184. stamp(now_t, door);
  185. if (target[0] == '#') {
  186. door::ANSIColor channel_color = door::ANSIColor{
  187. door::COLOR::YELLOW, door::COLOR::BLUE, door::ATTR::BOLD};
  188. door << channel_color << target << nick_color << "/" << nick_color
  189. << irc.nick << door::reset << " " << input << door::nl;
  190. } else {
  191. door << nick_color << irc.nick << " -> " << target << door::reset << " "
  192. << input << door::nl;
  193. }
  194. */
  195. }
  196. input.clear();
  197. input_scroll = 0;
  198. }
  199. // can't do /motd it matches /me /msg
  200. // nick matches names.
  201. const char *hot_keys[] = {"/join #", "/part #", "/talkto ", "/help", "/quit "};
  202. bool check_for_input(door::Door &door, ircClient &irc) {
  203. int c;
  204. int width = door.width;
  205. int third = width / 3;
  206. // return true when we have input and is "valid" // ready
  207. if (prompt.empty()) {
  208. // ok, nothing has been displayed at this time.
  209. if (door.haskey()) {
  210. // something to do.
  211. c = door.sleep_key(1);
  212. if (c < 0) {
  213. // handle timeout/hangup/out of time
  214. if (c < -1) {
  215. if (!has_quit) {
  216. std::string quit = "QUIT :";
  217. if (c == -2)
  218. quit += "BBS User Dropped Connection";
  219. if (c == -3)
  220. quit += "BBS User Out of time";
  221. irc.write(quit);
  222. has_quit = true;
  223. }
  224. }
  225. return false;
  226. }
  227. if (c > 0x1000)
  228. return false;
  229. // How to handle "early" typing, we we're still connecting...
  230. // FAIL-WHALE (what if we part all channels?)
  231. if (irc.registered)
  232. // don't take any imput unless our talkto has been set.
  233. if (isprint(c)) {
  234. prompt = "[" + irc.talkto() + "]";
  235. door << prompt_color << prompt << input_color << " ";
  236. door << (char)c;
  237. input.append(1, c);
  238. }
  239. }
  240. return false;
  241. } else {
  242. // continue on with what we have displayed.
  243. c = door.sleep_ms_key(ms_input_delay);
  244. if (c < -1) {
  245. if (!has_quit) {
  246. irc.write("QUIT");
  247. has_quit = true;
  248. }
  249. } else if (c != -1) {
  250. /*
  251. c = door.sleep_key(1);
  252. if (c < 0) {
  253. // handle error
  254. return false;
  255. }
  256. */
  257. if (c > 0x1000)
  258. return false;
  259. if (isprint(c)) {
  260. // string length check / scroll support?
  261. if ((int)input.size() == max_input) {
  262. door << (char)7;
  263. return false;
  264. }
  265. door << (char)c;
  266. input.append(1, c);
  267. int pos = input.size() - input_scroll;
  268. if (input_scroll != 0)
  269. pos += 3;
  270. // need something better then magic number here.
  271. // 3 is our extra padding here.
  272. int prompt_size = prompt.size() + 1; // prompt + space
  273. if (pos + prompt_size + 3 == width) {
  274. // Ok, scroll!
  275. clear_input(door);
  276. input_scroll = input.size() - third;
  277. restore_input(door);
  278. }
  279. // hot-keys
  280. if (input[0] == '/') {
  281. if (input.size() == 2) {
  282. char c = std::tolower(input[1]);
  283. if (!allow_part) {
  284. if (c == 'p')
  285. c = '!';
  286. }
  287. if (!allow_join) {
  288. if (c == 'j')
  289. c = '!';
  290. }
  291. for (auto hk : hot_keys) {
  292. if (c == hk[1]) {
  293. erase(door, input.size());
  294. input = hk;
  295. door << input;
  296. break;
  297. }
  298. }
  299. }
  300. }
  301. }
  302. if (c == 0x1b) {
  303. // escape key
  304. clear_input(door);
  305. input.clear();
  306. prompt.clear();
  307. input_scroll = 0;
  308. return false;
  309. }
  310. if ((c == 0x08) or (c == 0x7f)) {
  311. // hot-keys
  312. if (input[0] == '/') {
  313. for (auto hk : hot_keys) {
  314. if (input == hk) {
  315. clear_input(door);
  316. input.clear();
  317. prompt.clear();
  318. input_scroll = 0;
  319. return false;
  320. }
  321. }
  322. }
  323. if (input.size() > 1) {
  324. erase(door, 1);
  325. door << input_color;
  326. input.erase(input.length() - 1);
  327. if (input_scroll != 0) {
  328. // are we getting close?
  329. if ((int)input.size() - third < input_scroll) {
  330. // scroll the other way
  331. clear_input(door);
  332. input_scroll = (input.size() - 2 * third);
  333. if (input_scroll < 0)
  334. input_scroll = 0;
  335. restore_input(door);
  336. }
  337. }
  338. } else {
  339. // erasing the last character
  340. erase(door, 1);
  341. input.clear();
  342. erase(door, prompt.size() + 1);
  343. prompt.clear();
  344. return false;
  345. }
  346. }
  347. if (c == 0x0d) {
  348. clear_input(door);
  349. prompt.clear();
  350. parse_input(door, irc);
  351. input.clear();
  352. input_scroll = 0;
  353. return true;
  354. }
  355. }
  356. return false;
  357. }
  358. }