kbunix.c 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. /*
  2. * Copyright (c) 1995 Colin Plumb. All rights reserved.
  3. * For licensing and other legal details, see the file legal.c.
  4. *
  5. * kbunix.c - Unix keyboard input routines.
  6. */
  7. /*
  8. * Define NOTERMIO if you don't have the termios stuff
  9. */
  10. #include "first.h"
  11. #include <fcntl.h>
  12. #include <signal.h>
  13. #include <stdio.h>
  14. #include <stdlib.h> /* For exit() */
  15. #include <sys/types.h>
  16. /* How to get cbreak mode */
  17. #if defined(NOTERMIO)
  18. #include <sgtty.h> /* No termio: Use ioctl() TIOCGETP and TIOCSETP */
  19. #elif defined(SVR2)
  20. #include <termio.h> /* SVR2: Use ioctl() TCGETA and TCSETAF */
  21. #else /* Usual case */
  22. #include <termios.h> /* Posix: use tcgetattr/tcsetattr */
  23. #endif
  24. #ifdef sun /* including ioctl.h and termios.h gives a lot of warnings on sun */
  25. #include <sys/filio.h>
  26. #else
  27. #include <sys/ioctl.h> /* for FIONREAD */
  28. #endif /* sun */
  29. #ifndef FIONREAD
  30. #define FIONREAD TIOCINQ
  31. #endif
  32. #include "posix.h" /* For read(), sleep() */
  33. #include "kb.h"
  34. #if UNITTTEST
  35. #define randEvent(c) (void)c
  36. #else
  37. #include "random.h"
  38. #endif
  39. #include "kludge.h"
  40. /* The structure to hold the keyuboard's state */
  41. #if defined(NOTERMIO)
  42. static struct sgttyb kbState0, kbState1;
  43. #elif defined(SVR2)
  44. static struct termio kbState0, kbState1;
  45. #else
  46. static struct termios kbState0, kbState1;
  47. #endif
  48. #ifndef CBREAK
  49. #define CBREAK RAW
  50. #endif
  51. /* The basic task of getting the terminal into CBREAK mode. */
  52. static void
  53. kbInternalCbreak(int fd)
  54. {
  55. #ifdef NOTERMIO
  56. if (ioctl(fd, TIOCGETP, &kbState0) < 0) {
  57. fprintf (stderr, "\nUnable to get terminal characteristics: ");
  58. perror("ioctl");
  59. exit(1);
  60. }
  61. kbState1 = kbState0;
  62. kbState1.sg_flags |= CBREAK;
  63. kbState1.sg_flags &= ~ECHO;
  64. ioctl(fd, TIOCSETP, &kbState1);
  65. #else /* !NOTERMIO - the usual case */
  66. #ifdef SVR2
  67. if (ioctl(fd, TCGETA, &kbState0) < 0)
  68. #else
  69. if (tcgetattr(fd, &kbState0) < 0)
  70. #endif
  71. {
  72. fprintf (stderr, "\nUnable to get terminal characteristics: ");
  73. perror("ioctl");
  74. exit(1);
  75. }
  76. kbState1 = kbState0;
  77. kbState1.c_cc[VMIN] = 1;
  78. kbState1.c_cc[VTIME] = 0;
  79. kbState1.c_lflag &= ~(ECHO|ICANON);
  80. #ifdef SVR2
  81. ioctl(fd, TCSETAF, &kbState1);
  82. #else
  83. tcsetattr(fd, TCSAFLUSH, &kbState1);
  84. #endif /* not SVR2 */
  85. #endif /* !NOTERMIO */
  86. }
  87. /* Restore the terminal to normal operation */
  88. static void
  89. kbInternalNorm(int fd)
  90. {
  91. #if defined(NOTERMIO)
  92. ioctl(fd, TIOCSETP, &kbState0);
  93. #elif defined(SVR2)
  94. ioctl(fd, TCSETAF, &kbState0);
  95. #else /* Usual case */
  96. tcsetattr (fd, TCSAFLUSH, &kbState0);
  97. #endif
  98. }
  99. /* State variables */
  100. static volatile int kbCbreakFlag = 0;
  101. static int kbFd = -1;
  102. #ifdef SVR2
  103. static int (*savesig)(int);
  104. #else
  105. static void (*savesig)(int);
  106. #endif
  107. /* A wrapper around SIGINT and SIGCONT to restore the terminal modes. */
  108. static void
  109. kbSig1(int sig)
  110. {
  111. if (kbCbreakFlag)
  112. kbInternalNorm(kbFd);
  113. if (sig == SIGINT)
  114. signal(sig, savesig);
  115. else
  116. signal(sig, SIG_DFL);
  117. raise(sig); /* Re-send the signal */
  118. }
  119. static void
  120. kbAddSigs(void);
  121. /* Resume cbreak after SIGCONT */
  122. static void
  123. kbSig2(int sig)
  124. {
  125. (void)sig;
  126. if (kbCbreakFlag)
  127. kbInternalCbreak(kbFd);
  128. else
  129. kbAddSigs();
  130. }
  131. static void
  132. kbAddSigs(void)
  133. {
  134. savesig = signal (SIGINT, kbSig1);
  135. #ifdef SIGTSTP
  136. signal (SIGCONT, kbSig2);
  137. signal (SIGTSTP, kbSig1);
  138. #endif
  139. }
  140. static void
  141. kbRemoveSigs(void)
  142. {
  143. signal (SIGINT, savesig);
  144. #ifdef SIGTSTP
  145. signal (SIGCONT, SIG_DFL);
  146. signal (SIGTSTP, SIG_DFL);
  147. #endif
  148. }
  149. /* Now, at last, the externally callable functions */
  150. void
  151. kbCbreak(void)
  152. {
  153. if (kbFd < 0) {
  154. kbFd = open("/dev/tty", O_RDWR);
  155. if (kbFd < 0) {
  156. fputs("Can't open tty; using stdin\n", stderr);
  157. kbFd = STDIN_FILENO;
  158. }
  159. }
  160. kbAddSigs();
  161. kbCbreakFlag = 1;
  162. kbInternalCbreak(kbFd);
  163. }
  164. void
  165. kbNorm(void)
  166. {
  167. kbInternalNorm(kbFd);
  168. kbCbreakFlag = 0;
  169. kbRemoveSigs();
  170. }
  171. int
  172. kbGet(void)
  173. {
  174. int i;
  175. char c;
  176. i = read(kbFd, &c, 1);
  177. if (i < 1)
  178. return -1;
  179. randEvent(c);
  180. return c;
  181. }
  182. /*
  183. * Flush any pending input. If "thorough" is set, tries to be more
  184. * thorough about it. Ideally, wait for 1 second of quiet, but we
  185. * may do something more primitive.
  186. *
  187. * kbCbreak() has the side effect of flushing the inout queue, so this
  188. * is not too critical.
  189. */
  190. void
  191. kbFlush(int thorough)
  192. {
  193. if (thorough)
  194. sleep(1);
  195. #if defined(TCIFLUSH)
  196. tcflush(kbFd, TCIFLUSH);
  197. #elif defined(TIOCFLUSH)
  198. #ifndef FREAD
  199. #define FREAD 1 /* The usual value */
  200. #endif
  201. ioctl(kbFd, TIOCFLUSH, FREAD);
  202. #endif
  203. }
  204. #if UNITTEST /* Self-contained test driver */
  205. #include <ctype.h>
  206. int
  207. main(void)
  208. {
  209. int c;
  210. puts("Going to cbreak mode...");
  211. kbCbreak();
  212. puts("In cbreak mode. Please type.");
  213. for (;;) {
  214. c = kbGet();
  215. if (c == '\n' || c == '\r')
  216. break;
  217. printf("c = %d = '%c'\n", c, c);
  218. kbFlush(isupper(c));
  219. }
  220. puts("Returning to normal mode...");
  221. kbNorm();
  222. puts("Done.");
  223. return 0;
  224. }
  225. #endif /* UNITTEST */