Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.
 
 
 
 
 
 

305 řádky
7.8 KiB

  1. /*
  2. * term swallow a terminal-based application
  3. * Copyright (c) 2006 Sam Hocevar <sam@zoy.org>
  4. * All Rights Reserved
  5. *
  6. * $Id$
  7. *
  8. * This program is free software; you can redistribute it and/or
  9. * modify it under the terms of the Do What The Fuck You Want To
  10. * Public License, Version 2, as published by Sam Hocevar. See
  11. * http://sam.zoy.org/wtfpl/COPYING for more details.
  12. */
  13. #include "config.h"
  14. #include "common.h"
  15. #if !defined __KERNEL__
  16. # include <stdio.h>
  17. # include <string.h>
  18. # include <stdlib.h>
  19. # include <unistd.h>
  20. # include <fcntl.h>
  21. # include <sys/ioctl.h>
  22. # if defined HAVE_PTY_H
  23. # include <pty.h> /* for openpty and forkpty */
  24. # else
  25. # include <util.h> /* for OS X */
  26. # endif
  27. #endif
  28. #include "cucul.h"
  29. #include "caca.h"
  30. #define XTAB 3
  31. #define YTAB 2
  32. struct screen
  33. {
  34. cucul_canvas_t *cv;
  35. int fd;
  36. unsigned char *buf;
  37. long int total;
  38. int w, h;
  39. };
  40. static int create_pty(char *cmd, unsigned int w, unsigned int h);
  41. static int set_tty_size(int fd, unsigned int w, unsigned int h);
  42. int main(int argc, char **argv)
  43. {
  44. static cucul_canvas_t *cv;
  45. static caca_display_t *dp;
  46. static struct screen *screen;
  47. int pty = 0, prevpty = 0, i, n, w, h, eof = 0, refresh = 1, command = 0;
  48. if(argc < 2)
  49. {
  50. fprintf(stderr, "usage: %s <cmd1> [<cmd2> [<cmd3> ...]]\n", argv[0]);
  51. fprintf(stderr, "eg. \"%s zsh bash\"\n", argv[0]);
  52. return 1;
  53. }
  54. cv = cucul_create_canvas(0, 0);
  55. dp = caca_create_display(cv);
  56. if(!dp)
  57. return 1;
  58. n = argc - 1;
  59. screen = malloc(n * sizeof(struct screen));
  60. w = cucul_get_canvas_width(cv);
  61. h = cucul_get_canvas_height(cv);
  62. w = w <= XTAB * n ? 1 : w - XTAB * n;
  63. h = h <= YTAB * n ? 1 : h - YTAB * n;
  64. for(i = 0; i < n; i++)
  65. screen[i].cv = cucul_create_canvas(w, h);
  66. cucul_set_color_ansi(cv, CUCUL_LIGHTRED, CUCUL_BLACK);
  67. cucul_draw_cp437_box(cv, (n - 1 - pty) * XTAB, pty * YTAB,
  68. w + (n - 1 - pty) * XTAB + 1, h + pty * YTAB + 1);
  69. caca_refresh_display(dp);
  70. for(i = 0; i < n; i++)
  71. {
  72. screen[i].buf = NULL;
  73. screen[i].total = 0;
  74. screen[i].fd = create_pty(argv[i + 1], w, h);
  75. if(screen[i].fd < 0)
  76. return -1;
  77. }
  78. for(;;)
  79. {
  80. struct timeval tv;
  81. fd_set fdset;
  82. caca_event_t ev;
  83. int i, maxfd = 0, ret;
  84. /* Read data, if any */
  85. FD_ZERO(&fdset);
  86. for(i = 0; i < n; i++)
  87. {
  88. FD_SET(screen[i].fd, &fdset);
  89. if(screen[i].fd > maxfd)
  90. maxfd = screen[i].fd;
  91. }
  92. tv.tv_sec = 0;
  93. tv.tv_usec = 50000;
  94. ret = select(maxfd + 1, &fdset, NULL, NULL, &tv);
  95. if(ret < 0)
  96. {
  97. if(errno == EINTR)
  98. ; /* We probably got a SIGWINCH, ignore it */
  99. else
  100. {
  101. for(i = 0; i < n; i++)
  102. if(screen[i].total)
  103. break;
  104. if(i == n)
  105. break;
  106. }
  107. }
  108. else if(ret) for(i = 0; i < n; i++)
  109. {
  110. if(FD_ISSET(screen[i].fd, &fdset))
  111. {
  112. ssize_t n;
  113. screen[i].buf = realloc(screen[i].buf, screen[i].total + 4096);
  114. n = read(screen[i].fd, screen[i].buf + screen[i].total, 4096);
  115. if(n > 0)
  116. screen[i].total += n;
  117. else if(n == 0 || errno != EWOULDBLOCK)
  118. eof = 1;
  119. }
  120. }
  121. for(i = 0; i < n; i++) if(screen[i].total)
  122. {
  123. unsigned long int bytes;
  124. bytes = cucul_import_memory(screen[i].cv, screen[i].buf, screen[i].total, "utf8");
  125. if(bytes > 0)
  126. {
  127. screen[i].total -= bytes;
  128. memmove(screen[i].buf, screen[i].buf + bytes, screen[i].total);
  129. refresh = 1;
  130. }
  131. }
  132. /* Get events, if any */
  133. ret = caca_get_event(dp, CACA_EVENT_ANY, &ev, 0);
  134. if(ret && (ev.type & CACA_EVENT_KEY_PRESS))
  135. {
  136. if(command)
  137. {
  138. command = 0;
  139. switch(ev.data.key.ch)
  140. {
  141. case CACA_KEY_CTRL_A:
  142. pty ^= prevpty;
  143. prevpty ^= pty;
  144. pty ^= prevpty;
  145. refresh = 1;
  146. break;
  147. case 'n':
  148. case ' ':
  149. case '\0':
  150. case CACA_KEY_CTRL_N:
  151. prevpty = pty;
  152. pty = (pty + 1) % n;
  153. refresh = 1;
  154. break;
  155. case 'p':
  156. case CACA_KEY_CTRL_P:
  157. prevpty = pty;
  158. pty = (pty + n - 1) % n;
  159. refresh = 1;
  160. break;
  161. }
  162. }
  163. else
  164. {
  165. switch(ev.data.key.ch)
  166. {
  167. case CACA_KEY_CTRL_A:
  168. command = 1; break;
  169. case CACA_KEY_UP:
  170. write(screen[pty].fd, "\x1b[A", 3); break;
  171. case CACA_KEY_DOWN:
  172. write(screen[pty].fd, "\x1b[B", 3); break;
  173. case CACA_KEY_RIGHT:
  174. write(screen[pty].fd, "\x1b[C", 3); break;
  175. case CACA_KEY_LEFT:
  176. write(screen[pty].fd, "\x1b[D", 3); break;
  177. default:
  178. write(screen[pty].fd, &ev.data.key.ch, 1); break;
  179. }
  180. }
  181. }
  182. else if(ret && (ev.type & CACA_EVENT_RESIZE))
  183. {
  184. w = cucul_get_canvas_width(cv);
  185. h = cucul_get_canvas_height(cv);
  186. w = w <= XTAB * n ? 1 : w - XTAB * n;
  187. h = h <= YTAB * n ? 1 : h - YTAB * n;
  188. for(i = 0; i < n; i++)
  189. {
  190. cucul_set_canvas_size(screen[i].cv, w, h);
  191. set_tty_size(screen[i].fd, w, h);
  192. }
  193. cucul_clear_canvas(cv);
  194. refresh = 1;
  195. }
  196. /* Refresh screen */
  197. if(refresh)
  198. {
  199. refresh = 0;
  200. for(i = 0; i < n; i++)
  201. {
  202. int j = (pty + n - 1 - i) % n;
  203. cucul_blit(cv, (n - 1 - j) * XTAB + 1,
  204. j * YTAB + 1, screen[j].cv, NULL);
  205. cucul_draw_cp437_box(cv, (n - 1 - j) * XTAB, j * YTAB,
  206. w + (n - 1 - j) * XTAB + 1, h + j * YTAB + 1);
  207. }
  208. caca_refresh_display(dp);
  209. }
  210. if(eof)
  211. {
  212. for(i = 0; i < n; i++)
  213. if(screen[i].total)
  214. break;
  215. if(i == n)
  216. break;
  217. }
  218. }
  219. caca_get_event(dp, CACA_EVENT_KEY_PRESS, NULL, -1);
  220. /* Clean up */
  221. caca_free_display(dp);
  222. cucul_free_canvas(cv);
  223. for(i = 0; i < n; i++)
  224. cucul_free_canvas(screen[i].cv);
  225. return 0;
  226. }
  227. static int create_pty(char *cmd, unsigned int w, unsigned int h)
  228. {
  229. char **argv;
  230. int fd;
  231. pid_t pid;
  232. pid = forkpty(&fd, NULL, NULL, NULL);
  233. if(pid < 0)
  234. {
  235. fprintf(stderr, "forkpty() error\n");
  236. return -1;
  237. }
  238. else if(pid == 0)
  239. {
  240. set_tty_size(0, w, h);
  241. putenv("CACA_DRIVER=slang");
  242. putenv("TERM=xterm");
  243. argv = malloc(2 * sizeof(char *));
  244. argv[0] = cmd;
  245. argv[1] = NULL;
  246. execvp(cmd, argv);
  247. fprintf(stderr, "execvp() error\n");
  248. return -1;
  249. }
  250. fcntl(fd, F_SETFL, O_NDELAY);
  251. return fd;
  252. #if 0
  253. fprintf(stderr, "forkpty() not available\n");
  254. return -1;
  255. #endif
  256. }
  257. static int set_tty_size(int fd, unsigned int w, unsigned int h)
  258. {
  259. struct winsize ws;
  260. memset(&ws, 0, sizeof(ws));
  261. ws.ws_row = h;
  262. ws.ws_col = w;
  263. ioctl(fd, TIOCSWINSZ, (char *)&ws);
  264. return 0;
  265. }