spectator.h 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532
  1. #ifndef __SPECTATOR_H
  2. #define __SPECTATOR_H
  3. #include <map>
  4. #include <vector>
  5. #include <mutex>
  6. #include <condition_variable>
  7. #include <functional>
  8. namespace spectator {
  9. template <typename SCREEN>
  10. struct Screens {
  11. struct data_t {
  12. std::vector<maudit::glyph> data;
  13. unsigned int w;
  14. unsigned int h;
  15. };
  16. struct sender_t {
  17. std::string name;
  18. time_t ts;
  19. size_t max_frame;
  20. std::map<size_t, data_t> datastream;
  21. size_t refcount;
  22. std::condition_variable cv;
  23. sender_t() : ts(0), max_frame(0), refcount(0) {}
  24. };
  25. std::map<SCREEN*, sender_t> players;
  26. std::mutex player_mutex;
  27. std::map< SCREEN*, std::vector<std::string> > messages;
  28. std::mutex message_mutex;
  29. void add(SCREEN& s, const std::string& name) {
  30. std::unique_lock<std::mutex> l(player_mutex);
  31. auto& tmp = players[&s];
  32. tmp.name = name;
  33. tmp.ts = ::time(NULL);
  34. // HACK
  35. // Connect parent screen's guts to this machinery.
  36. s.callback = std::bind(&Screens<SCREEN>::watching_callback, this,
  37. std::placeholders::_1, std::placeholders::_2);
  38. }
  39. struct list_entry_t {
  40. void* tag;
  41. std::string name;
  42. };
  43. std::map< time_t, std::vector<list_entry_t> > list() {
  44. std::map< time_t, std::vector<list_entry_t> > ret;
  45. std::unique_lock<std::mutex> l(player_mutex);
  46. for (const auto& i : players) {
  47. ret[i.second.ts].push_back(list_entry_t{(void*)i.first, i.second.name});
  48. }
  49. return ret;
  50. }
  51. void remove(SCREEN& s) {
  52. std::unique_lock<std::mutex> l(player_mutex);
  53. auto i = players.find(&s);
  54. if (i == players.end())
  55. return;
  56. i->first->callback = nullptr;
  57. i->second.cv.notify_all();
  58. players.erase(i);
  59. }
  60. bool link(void* tag, SCREEN& another) {
  61. std::unique_lock<std::mutex> l(player_mutex);
  62. auto i = players.find((SCREEN*)tag);
  63. if (i == players.end())
  64. return false;
  65. sender_t& sender = i->second;
  66. sender.refcount++;
  67. return true;
  68. }
  69. void unlink(void* tag, SCREEN& another) {
  70. std::unique_lock<std::mutex> l(player_mutex);
  71. auto i = players.find((SCREEN*)tag);
  72. if (i == players.end())
  73. return;
  74. sender_t& sender = i->second;
  75. sender.refcount--;
  76. }
  77. bool notify(void* tag) {
  78. std::unique_lock<std::mutex> l(player_mutex);
  79. auto i = players.find((SCREEN*)tag);
  80. if (i == players.end())
  81. return false;
  82. sender_t& sender = i->second;
  83. sender.cv.notify_all();
  84. return true;
  85. }
  86. void send_message(void* tag, const std::string& message) {
  87. std::unique_lock<std::mutex> l(message_mutex);
  88. messages[(SCREEN*)tag].push_back(message);
  89. }
  90. bool get_messages(SCREEN& tag, std::vector<std::string>& out) {
  91. std::unique_lock<std::mutex> l(message_mutex);
  92. auto i = messages.find(&tag);
  93. if (i != messages.end()) {
  94. messages[&tag].swap(out);
  95. return true;
  96. }
  97. return false;
  98. }
  99. // HARCODED VALUES
  100. static const size_t MAX_FRAMES = 5;
  101. void watching_callback(SCREEN* parent, std::vector<maudit::glyph>& data) {
  102. std::unique_lock<std::mutex> l(player_mutex);
  103. auto i = players.find(parent);
  104. if (i == players.end())
  105. return;
  106. sender_t& sender = i->second;
  107. ++(sender.max_frame);
  108. auto& ds = sender.datastream[sender.max_frame];
  109. ds.data.swap(data);
  110. ds.w = parent->w;
  111. ds.h = parent->h;
  112. while (sender.datastream.size() > MAX_FRAMES) {
  113. sender.datastream.erase(sender.datastream.begin());
  114. }
  115. sender.cv.notify_all();
  116. }
  117. // Tri-state return value:
  118. // -1 means a lost connection,
  119. // 1 means a valid frame,
  120. // 0 means we tried to wait for a frame and need to call this function again. (No frame is returned.)
  121. int get_next_data(void* tag, data_t& out, size_t& last_frame_no) {
  122. SCREEN* parent = (SCREEN*)tag;
  123. std::unique_lock<std::mutex> l(player_mutex);
  124. auto i = players.find(parent);
  125. if (i == players.end()) {
  126. return -1;
  127. }
  128. sender_t& sender = i->second;
  129. auto j = sender.datastream.upper_bound(last_frame_no);
  130. if (j == sender.datastream.end()) {
  131. sender.cv.wait(l);
  132. return 0;
  133. }
  134. out = j->second;
  135. last_frame_no = j->first;
  136. return 1;
  137. }
  138. };
  139. template <typename SCREEN>
  140. Screens<SCREEN>& screens() {
  141. static Screens<SCREEN> ret;
  142. return ret;
  143. }
  144. void add_message_line(std::vector<maudit::glyph>& data, unsigned int w, unsigned int h, const std::string& message) {
  145. if (message.empty())
  146. return;
  147. if (w < 5)
  148. return;
  149. size_t x = 0;
  150. size_t i = (h - 1) * w;
  151. std::string gt(">");
  152. std::string space(" ");
  153. while (x < 3) {
  154. data[i].set_text(gt);
  155. data[i].fore = maudit::color::bright_yellow;
  156. data[i].back = maudit::color::bright_black;
  157. ++x;
  158. ++i;
  159. }
  160. data[i].set_text(space);
  161. data[i].fore = maudit::color::bright_yellow;
  162. data[i].back = maudit::color::bright_black;
  163. ++x;
  164. ++i;
  165. for (unsigned char c : message) {
  166. if (x >= w)
  167. break;
  168. data[i].set_text(std::string(1, c));
  169. data[i].fore = maudit::color::bright_white;
  170. data[i].back = maudit::color::bright_black;
  171. ++x;
  172. ++i;
  173. }
  174. while (x < w) {
  175. data[i].set_text(space);
  176. data[i].fore = maudit::color::bright_white;
  177. data[i].back = maudit::color::bright_black;
  178. ++x;
  179. ++i;
  180. }
  181. }
  182. template <typename SCREEN>
  183. bool copy_screen(const std::vector<maudit::glyph>& data, const std::string& message,
  184. unsigned int ow, unsigned int oh, unsigned int offx, unsigned int offy,
  185. SCREEN& target) {
  186. if (target.w == ow && target.h == oh && message.empty() && offx == 0 && offy == 0) {
  187. return target.send_screen(data);
  188. }
  189. unsigned int tw = target.w;
  190. unsigned int th = target.h;
  191. offy = std::min(offy, oh - 1);
  192. offx = std::min(offx, ow - 1);
  193. auto datai = data.begin() + (offy * ow) + offx;
  194. std::vector<maudit::glyph> temp;
  195. temp.resize(tw * th);
  196. unsigned int truw = std::min(tw, ow - offx);
  197. unsigned int truh = std::min(th, oh - offy);
  198. // Clip to an even-sized width.
  199. if (tw < ow && (truw & 1)) {
  200. truw--;
  201. }
  202. for (unsigned int y = 0; y < truh; ++y) {
  203. auto i = temp.begin() + (y * tw);
  204. auto j = datai + (y * ow);
  205. for (unsigned int x = 0; x < truw; ++x) {
  206. *i = *j;
  207. ++i;
  208. ++j;
  209. }
  210. }
  211. add_message_line(temp, tw, th, message);
  212. return target.send_screen(temp);
  213. }
  214. struct watcher_state_t {
  215. std::mutex mutex;
  216. bool done;
  217. std::string message;
  218. unsigned int offx;
  219. unsigned int offy;
  220. watcher_state_t() : done(false), offx(0), offy(0) {}
  221. };
  222. template <typename SCREEN>
  223. void watcher_input_thread(SCREEN& screen, void* tag, watcher_state_t& state) {
  224. while (1) {
  225. maudit::keypress k;
  226. if (!screen.wait_key(k) || k.key == maudit::keycode::esc) {
  227. screens<SCREEN>().notify(tag);
  228. std::unique_lock<std::mutex> l(state.mutex);
  229. state.done = true;
  230. return;
  231. }
  232. unsigned char cc = '\0';
  233. if (k.letter == '\n' || (k.letter >= ' ' && k.letter <= '~')) {
  234. cc = k.letter;
  235. } else if (k.letter == '\x7F' || k.letter == '\x08' || k.key == maudit::keycode::del) {
  236. cc = '\x08';
  237. }
  238. std::unique_lock<std::mutex> l(state.mutex);
  239. if (state.done)
  240. return;
  241. if (cc == '\n') {
  242. screens<SCREEN>().send_message(tag, state.message);
  243. state.message.clear();
  244. screens<SCREEN>().notify(tag);
  245. } else if (cc == '\x08' && state.message.size() > 0) {
  246. state.message.resize(state.message.size() - 1);
  247. screens<SCREEN>().notify(tag);
  248. } else if (cc != '\0' && state.message.size() < 60) {
  249. state.message += cc;
  250. screens<SCREEN>().notify(tag);
  251. } else if (k.key == maudit::keycode::right) {
  252. state.offx += 2;
  253. screens<SCREEN>().notify(tag);
  254. } else if (k.key == maudit::keycode::left && state.offx > 0) {
  255. state.offx -= 2;
  256. screens<SCREEN>().notify(tag);
  257. } else if (k.key == maudit::keycode::down) {
  258. state.offy++;
  259. screens<SCREEN>().notify(tag);
  260. } else if (k.key == maudit::keycode::up && state.offy > 0) {
  261. state.offy--;
  262. screens<SCREEN>().notify(tag);
  263. }
  264. }
  265. }
  266. template <typename SCREEN>
  267. void choose_and_watch(SCREEN& screen) {
  268. grender::Grid render;
  269. render.init(0, 0);
  270. auto& themes = render.ui_symbol_themes;
  271. for (const auto& i : constants().ui_symbols) {
  272. // HACK!
  273. themes.push_back(i[1]);
  274. }
  275. render.set_ui_symbol();
  276. while (1) {
  277. time_t now = ::time(NULL);
  278. auto games = screens<SCREEN>().list();
  279. std::string window =
  280. " When viewing a game, press 'ESC' twice to stop and return to this screen.\n"
  281. " Use the arrow keys to scroll your view in case the player's window is larger than yours.\n"
  282. " Simply start typing and press 'Enter' to send a chat message.\n\n"
  283. "\2Active games: (press space to refresh)\3\n\n"_m;
  284. char c = 'a';
  285. std::map<char, void*> games_c;
  286. for (const auto& i : games) {
  287. for (const auto& j : i.second) {
  288. std::string name = j.name;
  289. if (name.empty())
  290. name = "anonymous"_m;
  291. if (name == "_")
  292. continue;
  293. window += nlp::message("%c) %S, started playing %d minutes ago.\n"_m, c, name, (now - i.first) / 60);
  294. games_c[c] = j.tag;
  295. ++c;
  296. }
  297. }
  298. maudit::keypress k;
  299. try {
  300. k = render.draw_window(screen, window);
  301. } catch (...) {
  302. return;
  303. }
  304. if (k.key == maudit::keycode::esc) {
  305. return;
  306. }
  307. auto i = games_c.find(k.letter);
  308. if (i == games_c.end())
  309. continue;
  310. void* parent = i->second;
  311. {
  312. watcher_state_t state;
  313. screens<SCREEN>().link(parent, screen);
  314. std::thread thread(watcher_input_thread<SCREEN>, std::ref(screen), parent, std::ref(state));
  315. typename Screens<SCREEN>::data_t data;
  316. size_t last_frame_no = 0;
  317. while (1) {
  318. int ret = screens<SCREEN>().get_next_data(parent, data, last_frame_no);
  319. if (ret < 0) {
  320. {
  321. std::unique_lock<std::mutex> l(state.mutex);
  322. if (state.done)
  323. break;
  324. }
  325. // Lost connection.
  326. // Spin about doing nothing, keeping the last screen in view.
  327. ::sleep(1);
  328. continue;
  329. }
  330. std::string tmp;
  331. unsigned int offx;
  332. unsigned int offy;
  333. {
  334. std::unique_lock<std::mutex> l(state.mutex);
  335. if (state.done)
  336. break;
  337. tmp = state.message;
  338. if (state.offx > data.w) {
  339. state.offx = data.w;
  340. if (state.offx & 1)
  341. ++state.offx;
  342. }
  343. if (state.offy > data.h) {
  344. state.offy = data.h;
  345. }
  346. offx = state.offx;
  347. offy = state.offy;
  348. }
  349. if (!copy_screen(data.data, tmp, data.w, data.h, offx, offy, screen)) {
  350. std::unique_lock<std::mutex> l(state.mutex);
  351. state.done = true;
  352. break;
  353. }
  354. }
  355. thread.join();
  356. screens<SCREEN>().unlink(parent, screen);
  357. }
  358. }
  359. }
  360. }
  361. #endif