tmate-encoder.c 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463
  1. #include "tmate.h"
  2. #include "tmate-protocol.h"
  3. #include "window-copy.h"
  4. #define pack(what, ...) _pack(&tmate_session.encoder, what, ##__VA_ARGS__)
  5. void tmate_write_header(void)
  6. {
  7. pack(array, 3);
  8. pack(int, TMATE_OUT_HEADER);
  9. pack(int, TMATE_PROTOCOL_VERSION);
  10. pack(string, VERSION);
  11. }
  12. void tmate_write_ready(void)
  13. {
  14. pack(array, 1);
  15. pack(int, TMATE_OUT_READY);
  16. }
  17. void tmate_sync_layout(void)
  18. {
  19. struct session *s;
  20. struct winlink *wl;
  21. struct window *w;
  22. struct window_pane *wp;
  23. int num_panes = 0;
  24. int num_windows = 0;
  25. int active_pane_id;
  26. int active_window_idx = -1;
  27. /*
  28. * TODO this can get a little heavy.
  29. * We are shipping the full layout whenever a window name changes,
  30. * that is, at every shell command.
  31. * Might be better to do something incremental.
  32. */
  33. /*
  34. * We only allow one session, it makes our lives easier.
  35. * Especially when the HTML5 client will come along.
  36. * We make no distinction between a winlink and its window except
  37. * that we send the winlink idx to draw the status bar properly.
  38. */
  39. s = RB_MIN(sessions, &sessions);
  40. if (!s)
  41. return;
  42. num_windows = 0;
  43. RB_FOREACH(wl, winlinks, &s->windows) {
  44. if (wl->window)
  45. num_windows++;
  46. }
  47. if (!num_windows)
  48. return;
  49. pack(array, 5);
  50. pack(int, TMATE_OUT_SYNC_LAYOUT);
  51. pack(int, s->sx);
  52. pack(int, s->sy);
  53. pack(array, num_windows);
  54. RB_FOREACH(wl, winlinks, &s->windows) {
  55. w = wl->window;
  56. if (!w)
  57. continue;
  58. w->tmate_last_sync_active_pane = NULL;
  59. active_pane_id = -1;
  60. if (active_window_idx == -1)
  61. active_window_idx = wl->idx;
  62. pack(array, 4);
  63. pack(int, wl->idx);
  64. pack(string, w->name);
  65. num_panes = 0;
  66. TAILQ_FOREACH(wp, &w->panes, entry)
  67. num_panes++;
  68. pack(array, num_panes);
  69. TAILQ_FOREACH(wp, &w->panes, entry) {
  70. pack(array, 5);
  71. pack(int, wp->id);
  72. pack(int, wp->sx);
  73. pack(int, wp->sy);
  74. pack(int, wp->xoff);
  75. pack(int, wp->yoff);
  76. if (wp == w->active) {
  77. w->tmate_last_sync_active_pane = wp;
  78. active_pane_id = wp->id;
  79. }
  80. }
  81. pack(int, active_pane_id);
  82. }
  83. if (s->curw)
  84. active_window_idx = s->curw->idx;
  85. pack(int, active_window_idx);
  86. }
  87. /* TODO add a buffer for pty_data ? */
  88. #define TMATE_MAX_PTY_SIZE (16*1024)
  89. void tmate_pty_data(struct window_pane *wp, const char *buf, size_t len)
  90. {
  91. size_t to_write;
  92. while (len > 0) {
  93. to_write = len < TMATE_MAX_PTY_SIZE ? len : TMATE_MAX_PTY_SIZE;
  94. pack(array, 3);
  95. pack(int, TMATE_OUT_PTY_DATA);
  96. pack(int, wp->id);
  97. pack(str, to_write);
  98. pack(str_body, buf, to_write);
  99. buf += to_write;
  100. len -= to_write;
  101. }
  102. }
  103. extern const struct cmd_entry cmd_bind_key_entry;
  104. extern const struct cmd_entry cmd_unbind_key_entry;
  105. extern const struct cmd_entry cmd_set_option_entry;
  106. extern const struct cmd_entry cmd_set_window_option_entry;
  107. static const struct cmd_entry *replicated_cmds[] = {
  108. &cmd_bind_key_entry,
  109. &cmd_unbind_key_entry,
  110. &cmd_set_option_entry,
  111. &cmd_set_window_option_entry,
  112. NULL
  113. };
  114. int tmate_should_replicate_cmd(const struct cmd_entry *cmd)
  115. {
  116. const struct cmd_entry **ptr;
  117. for (ptr = replicated_cmds; *ptr; ptr++)
  118. if (*ptr == cmd)
  119. return 1;
  120. return 0;
  121. }
  122. #define sc (&session->saved_tmux_cmds)
  123. #define SAVED_TMUX_CMD_INITIAL_SIZE 256
  124. static void __tmate_exec_cmd_args(int argc, const char **argv);
  125. static void append_saved_cmd(struct tmate_session *session,
  126. int argc, const char **argv)
  127. {
  128. if (!sc->cmds) {
  129. sc->capacity = SAVED_TMUX_CMD_INITIAL_SIZE;
  130. sc->cmds = xmalloc(sizeof(*sc->cmds) * sc->capacity);
  131. sc->tail = 0;
  132. }
  133. if (sc->tail == sc->capacity) {
  134. sc->capacity *= 2;
  135. sc->cmds = xrealloc(sc->cmds, sizeof(*sc->cmds) * sc->capacity);
  136. }
  137. sc->cmds[sc->tail].argc = argc;
  138. sc->cmds[sc->tail].argv = cmd_copy_argv(argc, (char **)argv);
  139. sc->tail++;
  140. }
  141. static void replay_saved_cmd(struct tmate_session *session)
  142. {
  143. unsigned int i;
  144. for (i = 0; i < sc->tail; i++)
  145. __tmate_exec_cmd_args(sc->cmds[i].argc, (const char **)sc->cmds[i].argv);
  146. }
  147. #undef sc
  148. struct args_entry {
  149. u_char flag;
  150. char *value;
  151. RB_ENTRY(args_entry) entry;
  152. };
  153. static void extract_cmd(struct cmd *cmd, int *_argc, char ***_argv)
  154. {
  155. struct args_entry *entry;
  156. struct args* args = cmd->args;
  157. int argc = 0;
  158. char **argv;
  159. int next = 0, i;
  160. argc++; /* cmd name */
  161. RB_FOREACH(entry, args_tree, &args->tree) {
  162. argc++;
  163. if (entry->value != NULL)
  164. argc++;
  165. }
  166. argc += args->argc;
  167. argv = xmalloc(sizeof(char *) * argc);
  168. argv[next++] = xstrdup(cmd->entry->name);
  169. RB_FOREACH(entry, args_tree, &args->tree) {
  170. xasprintf(&argv[next++], "-%c", entry->flag);
  171. if (entry->value != NULL)
  172. argv[next++] = xstrdup(entry->value);
  173. }
  174. for (i = 0; i < args->argc; i++)
  175. argv[next++] = xstrdup(args->argv[i]);
  176. *_argc = argc;
  177. *_argv = argv;
  178. }
  179. static void __tmate_exec_cmd_args(int argc, const char **argv)
  180. {
  181. int i;
  182. pack(array, argc + 1);
  183. pack(int, TMATE_OUT_EXEC_CMD);
  184. for (i = 0; i < argc; i++)
  185. pack(string, argv[i]);
  186. }
  187. void tmate_exec_cmd_args(int argc, const char **argv)
  188. {
  189. __tmate_exec_cmd_args(argc, argv);
  190. append_saved_cmd(&tmate_session, argc, argv);
  191. }
  192. void tmate_exec_cmd(struct cmd *cmd)
  193. {
  194. int argc;
  195. char **argv;
  196. extract_cmd(cmd, &argc, &argv);
  197. tmate_exec_cmd_args(argc, (const char **)argv);
  198. cmd_free_argv(argc, argv);
  199. }
  200. void tmate_failed_cmd(int client_id, const char *cause)
  201. {
  202. pack(array, 3);
  203. pack(int, TMATE_OUT_FAILED_CMD);
  204. pack(int, client_id);
  205. pack(string, cause);
  206. }
  207. void tmate_status(const char *left, const char *right)
  208. {
  209. static char *old_left, *old_right;
  210. if (old_left && !strcmp(old_left, left) &&
  211. old_right && !strcmp(old_right, right))
  212. return;
  213. pack(array, 3);
  214. pack(int, TMATE_OUT_STATUS);
  215. pack(string, left);
  216. pack(string, right);
  217. free(old_left);
  218. free(old_right);
  219. old_left = xstrdup(left);
  220. old_right = xstrdup(right);
  221. }
  222. void tmate_sync_copy_mode(struct window_pane *wp)
  223. {
  224. struct window_copy_mode_data *data = wp->modedata;
  225. pack(array, 3);
  226. pack(int, TMATE_OUT_SYNC_COPY_MODE);
  227. pack(int, wp->id);
  228. if (wp->mode != &window_copy_mode ||
  229. data->inputtype == WINDOW_COPY_PASSWORD) {
  230. pack(array, 0);
  231. return;
  232. }
  233. pack(array, 6);
  234. pack(int, data->backing == &wp->base);
  235. pack(int, data->oy);
  236. pack(int, data->cx);
  237. pack(int, data->cy);
  238. if (data->screen.sel.flag) {
  239. pack(array, 3);
  240. pack(int, data->selx);
  241. pack(int, -data->sely + screen_hsize(data->backing)
  242. + screen_size_y(data->backing) - 1);
  243. pack(int, data->rectflag);
  244. } else
  245. pack(array, 0);
  246. if (data->inputprompt) {
  247. pack(array, 3);
  248. pack(int, data->inputtype);
  249. pack(string, data->inputprompt);
  250. pack(string, data->inputstr);
  251. } else
  252. pack(array, 0);
  253. }
  254. void tmate_write_copy_mode(struct window_pane *wp, const char *str)
  255. {
  256. pack(array, 3);
  257. pack(int, TMATE_OUT_WRITE_COPY_MODE);
  258. pack(int, wp->id);
  259. pack(string, str);
  260. }
  261. void tmate_write_fin(void)
  262. {
  263. pack(array, 1);
  264. pack(int, TMATE_OUT_FIN);
  265. }
  266. static void do_snapshot_grid(struct grid *grid, unsigned int max_history_lines)
  267. {
  268. struct grid_line *line;
  269. struct grid_cell gc;
  270. unsigned int line_i, i;
  271. unsigned int max_lines;
  272. size_t str_len;
  273. max_lines = max_history_lines + grid->sy;
  274. #define grid_num_lines(grid) (grid->hsize + grid->sy)
  275. if (grid_num_lines(grid) > max_lines)
  276. line_i = grid_num_lines(grid) - max_lines;
  277. else
  278. line_i = 0;
  279. pack(array, grid_num_lines(grid) - line_i);
  280. for (; line_i < grid_num_lines(grid); line_i++) {
  281. line = &grid->linedata[line_i];
  282. pack(array, 2);
  283. str_len = 0;
  284. for (i = 0; i < line->cellsize; i++) {
  285. grid_get_cell(grid, i, line_i, &gc);
  286. str_len += gc.data.size;
  287. }
  288. pack(str, str_len);
  289. for (i = 0; i < line->cellsize; i++) {
  290. grid_get_cell(grid, i, line_i, &gc);
  291. pack(str_body, gc.data.data, gc.data.size);
  292. }
  293. pack(array, line->cellsize);
  294. for (i = 0; i < line->cellsize; i++) {
  295. grid_get_cell(grid, i, line_i, &gc);
  296. pack(unsigned_int, ((gc.flags << 24) |
  297. (gc.attr << 16) |
  298. (gc.bg << 8) |
  299. gc.fg ));
  300. }
  301. }
  302. }
  303. static void do_snapshot_pane(struct window_pane *wp, unsigned int max_history_lines)
  304. {
  305. struct screen *screen = &wp->base;
  306. pack(array, 4);
  307. pack(int, wp->id);
  308. pack(unsigned_int, screen->mode);
  309. pack(array, 3);
  310. pack(int, screen->cx);
  311. pack(int, screen->cy);
  312. do_snapshot_grid(screen->grid, max_history_lines);
  313. if (wp->saved_grid) {
  314. pack(array, 3);
  315. pack(int, wp->saved_cx);
  316. pack(int, wp->saved_cy);
  317. do_snapshot_grid(wp->saved_grid, max_history_lines);
  318. } else {
  319. pack(nil);
  320. }
  321. }
  322. static void tmate_send_session_snapshot(unsigned int max_history_lines)
  323. {
  324. struct session *s;
  325. struct winlink *wl;
  326. struct window *w;
  327. struct window_pane *pane;
  328. int num_panes;
  329. pack(array, 2);
  330. pack(int, TMATE_OUT_SNAPSHOT);
  331. s = RB_MIN(sessions, &sessions);
  332. if (!s)
  333. tmate_fatal("no session?");
  334. num_panes = 0;
  335. RB_FOREACH(wl, winlinks, &s->windows) {
  336. w = wl->window;
  337. if (!w)
  338. continue;
  339. TAILQ_FOREACH(pane, &w->panes, entry)
  340. num_panes++;
  341. }
  342. pack(array, num_panes);
  343. RB_FOREACH(wl, winlinks, &s->windows) {
  344. w = wl->window;
  345. if (!w)
  346. continue;
  347. TAILQ_FOREACH(pane, &w->panes, entry)
  348. do_snapshot_pane(pane, max_history_lines);
  349. }
  350. }
  351. static void tmate_send_reconnection_data(struct tmate_session *session)
  352. {
  353. if (!session->reconnection_data)
  354. return;
  355. pack(array, 2);
  356. pack(int, TMATE_OUT_RECONNECT);
  357. pack(string, session->reconnection_data);
  358. }
  359. #define RECONNECTION_MAX_HISTORY_LINE 300
  360. void tmate_send_reconnection_state(struct tmate_session *session)
  361. {
  362. /* Start with a fresh encoder */
  363. tmate_encoder_destroy(&session->encoder);
  364. tmate_encoder_init(&session->encoder, NULL, session);
  365. tmate_write_header();
  366. tmate_send_reconnection_data(session);
  367. replay_saved_cmd(session);
  368. /* TODO send all option variables */
  369. tmate_write_ready();
  370. tmate_sync_layout();
  371. tmate_send_session_snapshot(RECONNECTION_MAX_HISTORY_LINE);
  372. }