term.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598
  1. /*
  2. term.c - terminal functions
  3. Copyright (c) 1999 - 2006 NoisyB
  4. Copyright (c) 2001 - 2005, 2015 dbjh
  5. Copyright (c) 2002 - 2004 Jan-Erik Karlsson (Amiga code)
  6. This program is free software; you can redistribute it and/or modify
  7. it under the terms of the GNU General Public License as published by
  8. the Free Software Foundation; either version 2 of the License, or
  9. (at your option) any later version.
  10. This program is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. GNU General Public License for more details.
  14. You should have received a copy of the GNU General Public License
  15. along with this program; if not, write to the Free Software
  16. Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  17. */
  18. // gcc term.c -o term -ltermcap -DUSE_TERMCAP
  19. // st_term_t and term_*() functions taken and modified from LAME frontend/console.c
  20. #ifdef HAVE_CONFIG_H
  21. #include "config.h"
  22. #endif
  23. #include <stdio.h>
  24. #include <string.h>
  25. #include <stdlib.h>
  26. #ifdef USE_TERMCAP
  27. #include <termcap.h>
  28. #endif
  29. #ifdef HAVE_UNISTD_H
  30. #include <unistd.h>
  31. #endif
  32. #ifdef _WIN32
  33. #ifdef _MSC_VER
  34. #pragma warning(push)
  35. #pragma warning(disable: 4255) // 'function' : no function prototype given: converting '()' to '(void)'
  36. #pragma warning(disable: 4668) // 'symbol' is not defined as a preprocessor macro, replacing with '0' for 'directives'
  37. #pragma warning(disable: 4820) // 'bytes' bytes padding added after construct 'member_name'
  38. #endif
  39. #include <io.h> // isatty() (MinGW)
  40. #include <windows.h> // HANDLE, SetConsoleTextAttribute()
  41. #ifdef _MSC_VER
  42. #pragma warning(pop)
  43. #endif
  44. #endif // _WIN32
  45. #ifdef DJGPP
  46. #include <dpmi.h> // needed for __dpmi_int() by ansi_init()
  47. #endif
  48. #include "misc/term.h"
  49. #include "ucon64_defines.h"
  50. #ifdef __CYGWIN__ // on Cygwin (gcc for Windows) we
  51. #define USE_POLL // need poll() for kbhit(). poll()
  52. #include <sys/poll.h> // is available on Linux, not on
  53. #endif // BeOS. DOS already has kbhit()
  54. #if (defined __unix__ && !defined __MSDOS__) || defined __BEOS__ || \
  55. defined __APPLE__ // Mac OS X actually
  56. #include <termios.h>
  57. typedef struct termios tty_t;
  58. #endif
  59. typedef struct
  60. {
  61. #if defined _WIN32 && !defined __CYGWIN__
  62. HANDLE Console_Handle;
  63. #endif
  64. int w; // display width
  65. int h; // display height
  66. char up[10];
  67. char clreoln[10];
  68. char emph[10];
  69. char norm[10];
  70. } st_term_t;
  71. static st_term_t term;
  72. int
  73. term_open (void)
  74. {
  75. st_term_t *t = &term;
  76. #ifdef USE_TERMCAP
  77. char *tp;
  78. char tc[10];
  79. int val;
  80. #endif
  81. // defaults
  82. memset (t, 0, sizeof (st_term_t));
  83. t->w = 80;
  84. t->h = 25;
  85. #if defined _WIN32 && !defined __CYGWIN__
  86. t->Console_Handle = GetStdHandle (STD_ERROR_HANDLE);
  87. #endif
  88. strcpy (t->up, "\033[A");
  89. #ifdef USE_TERMCAP
  90. // try to catch additional information about special termsole sequences
  91. #if 0
  92. if (!(term_name = getenv ("TERM")))
  93. return -1;
  94. if (tgetent (term_buff, term_name) != 1)
  95. return -1;
  96. #endif
  97. val = tgetnum ("co");
  98. if (val >= 40 && val <= 512)
  99. t->w = val;
  100. val = tgetnum ("li");
  101. if (val >= 16 && val <= 256)
  102. t->h = val;
  103. *(tp = tc) = 0;
  104. tp = tgetstr ("up", &tp);
  105. if (tp)
  106. strcpy (t->up, tp);
  107. *(tp = tc) = 0;
  108. tp = tgetstr ("ce", &tp);
  109. if (tp)
  110. strcpy (t->clreoln, tp);
  111. *(tp = tc) = 0;
  112. tp = tgetstr ("md", &tp);
  113. if (tp)
  114. strcpy (t->emph, tp);
  115. *(tp = tc) = 0;
  116. tp = tgetstr ("me", &tp);
  117. if (tp)
  118. strcpy (t->norm, tp);
  119. #endif // USE_TERMCAP
  120. return 0;
  121. }
  122. int
  123. term_close (void)
  124. {
  125. st_term_t *t = &term;
  126. if (t)
  127. {
  128. memset (t, 0, sizeof (st_term_t));
  129. // free (t);
  130. }
  131. return 0;
  132. }
  133. int
  134. term_w (void)
  135. {
  136. st_term_t *t = &term;
  137. return t->w;
  138. }
  139. int
  140. term_h (void)
  141. {
  142. st_term_t *t = &term;
  143. return t->h;
  144. }
  145. const char *
  146. term_up (void)
  147. {
  148. st_term_t *t = &term;
  149. return t->up;
  150. }
  151. const char *
  152. term_clreoln (void)
  153. {
  154. st_term_t *t = &term;
  155. return t->clreoln;
  156. }
  157. const char *
  158. term_emph (void)
  159. {
  160. st_term_t *t = &term;
  161. return t->emph;
  162. }
  163. const char *
  164. term_norm (void)
  165. {
  166. st_term_t *t = &term;
  167. return t->norm;
  168. }
  169. static int misc_ansi_color = 0;
  170. #if (defined __unix__ && !defined __MSDOS__) || defined __BEOS__ || \
  171. defined __APPLE__ // Mac OS X actually
  172. static void set_tty (tty_t *param);
  173. #endif
  174. #if defined _WIN32 && defined USE_ANSI_COLOR
  175. int
  176. vprintf2 (const char *format, va_list argptr)
  177. // Cheap hack to get the Visual C++ and MinGW ports support "ANSI colors".
  178. // Cheap, because it only supports the ANSI escape sequences uCON64 uses.
  179. {
  180. #undef printf
  181. #undef fprintf
  182. int n_chars = 0, n_ctrl = 0, n_print, done = 0;
  183. char output[MAXBUFSIZE], *ptr, *ptr2;
  184. HANDLE stdout_handle;
  185. CONSOLE_SCREEN_BUFFER_INFO info;
  186. WORD org_attr, new_attr = 0;
  187. n_chars = _vsnprintf (output, MAXBUFSIZE, format, argptr);
  188. if (n_chars == -1)
  189. {
  190. fprintf (stderr, "INTERNAL ERROR: Output buffer in vprintf2() is too small (%d bytes).\n"
  191. " Please send a bug report\n", MAXBUFSIZE);
  192. exit (1);
  193. }
  194. output[MAXBUFSIZE - 1] = 0;
  195. if ((ptr = strchr (output, 0x1b)) == NULL)
  196. fputs (output, stdout);
  197. else
  198. {
  199. stdout_handle = GetStdHandle (STD_OUTPUT_HANDLE);
  200. GetConsoleScreenBufferInfo (stdout_handle, &info);
  201. org_attr = info.wAttributes;
  202. if (ptr > output)
  203. {
  204. *ptr = 0;
  205. fputs (output, stdout);
  206. *ptr = 0x1b;
  207. }
  208. while (!done)
  209. {
  210. if (memcmp (ptr, "\x1b[0m", 4) == 0)
  211. {
  212. new_attr = org_attr;
  213. n_ctrl = 4;
  214. }
  215. else if (memcmp (ptr, "\x1b[01;31m", 8) == 0)
  216. {
  217. new_attr = FOREGROUND_INTENSITY | FOREGROUND_RED;
  218. n_ctrl = 8;
  219. }
  220. else if (memcmp (ptr, "\x1b[01;32m", 8) == 0)
  221. {
  222. new_attr = FOREGROUND_INTENSITY | FOREGROUND_GREEN;
  223. n_ctrl = 8;
  224. }
  225. else if (memcmp (ptr, "\x1b[01;33m", 8) == 0) // bright yellow
  226. {
  227. new_attr = FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_GREEN;
  228. n_ctrl = 8;
  229. }
  230. else if (memcmp (ptr, "\x1b[31;41m", 8) == 0)
  231. {
  232. new_attr = FOREGROUND_RED | BACKGROUND_RED;
  233. n_ctrl = 8;
  234. }
  235. else if (memcmp (ptr, "\x1b[32;42m", 8) == 0)
  236. {
  237. new_attr = FOREGROUND_GREEN | BACKGROUND_GREEN;
  238. n_ctrl = 8;
  239. }
  240. else if (memcmp (ptr, "\x1b[30;41m", 8) == 0) // 30 = foreground black
  241. {
  242. new_attr = BACKGROUND_RED;
  243. n_ctrl = 8;
  244. }
  245. else if (memcmp (ptr, "\x1b[30;42m", 8) == 0)
  246. {
  247. new_attr = BACKGROUND_GREEN;
  248. n_ctrl = 8;
  249. }
  250. else if (*ptr == 0x1b)
  251. {
  252. new_attr = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
  253. SetConsoleTextAttribute (stdout_handle, new_attr);
  254. printf ("\n"
  255. "INTERNAL WARNING: vprintf2() encountered an unsupported ANSI escape sequence\n"
  256. " Please send a bug report\n");
  257. n_ctrl = 0;
  258. }
  259. SetConsoleTextAttribute (stdout_handle, new_attr);
  260. ptr2 = strchr (ptr + 1, 0x1b);
  261. if (ptr2)
  262. n_print = ptr2 - ptr;
  263. else
  264. {
  265. n_print = strlen (ptr);
  266. done = 1;
  267. }
  268. ptr[n_print] = 0;
  269. ptr += n_ctrl;
  270. fputs (ptr, stdout);
  271. (ptr - n_ctrl)[n_print] = 0x1b;
  272. ptr = ptr2;
  273. }
  274. }
  275. return n_chars;
  276. #define printf printf2
  277. #define fprintf fprintf2
  278. }
  279. int
  280. printf2 (const char *format, ...)
  281. {
  282. va_list argptr;
  283. int n_chars;
  284. va_start (argptr, format);
  285. n_chars = vprintf2 (format, argptr);
  286. va_end (argptr);
  287. return n_chars;
  288. }
  289. int
  290. fprintf2 (FILE *file, const char *format, ...)
  291. {
  292. va_list argptr;
  293. int n_chars;
  294. va_start (argptr, format);
  295. if (file != stdout)
  296. n_chars = vfprintf (file, format, argptr);
  297. else
  298. n_chars = vprintf2 (format, argptr);
  299. va_end (argptr);
  300. return n_chars;
  301. }
  302. #endif // defined _WIN32 && defined USE_ANSI_COLOR
  303. void
  304. clear_line (void)
  305. /*
  306. This function is used to fix a problem when using the MinGW or Visual C++ port
  307. on Windows 98 (probably Windows 95 too) while ANSI.SYS is not loaded.
  308. If a line contains colors, printed with printf() or fprintf() (actually
  309. printf2() or fprintf2()), it cannot be cleared by printing spaces on the same
  310. line. A solution is using SetConsoleTextAttribute(). The problem doesn't occur
  311. if ANSI.SYS is loaded. It also doesn't occur on Windows XP, even if ANSI.SYS
  312. isn't loaded. We print 79 spaces (not 80), because in command.com the cursor
  313. advances to the next line if we print something on the 80th column (in 80
  314. column mode). This doesn't happen in xterm.
  315. */
  316. {
  317. #if !defined _WIN32 || !defined USE_ANSI_COLOR
  318. fputs ("\r \r", stdout);
  319. #else
  320. WORD org_attr;
  321. CONSOLE_SCREEN_BUFFER_INFO info;
  322. HANDLE stdout_handle = GetStdHandle (STD_OUTPUT_HANDLE);
  323. GetConsoleScreenBufferInfo (stdout_handle, &info);
  324. org_attr = info.wAttributes;
  325. SetConsoleTextAttribute (stdout_handle, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
  326. fputs ("\r \r", stdout);
  327. SetConsoleTextAttribute (stdout_handle, org_attr);
  328. #endif
  329. }
  330. int
  331. ansi_init (void)
  332. {
  333. int result = isatty (STDOUT_FILENO);
  334. #ifdef DJGPP
  335. if (result)
  336. {
  337. // Don't use __MSDOS__, because __dpmi_regs and __dpmi_int are DJGPP specific
  338. __dpmi_regs reg;
  339. reg.x.ax = 0x1a00; // DOS 4.0+ ANSI.SYS installation check
  340. __dpmi_int (0x2f, &reg);
  341. if (reg.h.al != 0xff) // AL == 0xff if installed
  342. result = 0;
  343. }
  344. #endif
  345. misc_ansi_color = result;
  346. return result;
  347. }
  348. int
  349. gauge (int percent, int width, char char1, char char2, int color1, int color2)
  350. {
  351. int x = 0;
  352. char buf[1024 + 32]; // 32 == ANSI code buffer
  353. if (!width || percent < 0 || percent > 100)
  354. return -1;
  355. if (width > 1024)
  356. width = 1024;
  357. x = (width * percent) / 100;
  358. memset (buf, char1, x);
  359. buf[x] = 0;
  360. if (x < width) // percent < 100
  361. {
  362. if (color1 != -1 && color2 != -1)
  363. sprintf (&buf[x], "\x1b[3%d;4%dm", color1, color1);
  364. memset (strchr (buf, 0), char2, width - x);
  365. }
  366. if (color1 != -1 && color2 != -1) // ANSI?
  367. {
  368. buf[width + 8] = 0; // 8 == ANSI code length
  369. fprintf (stdout, "\x1b[3%d;4%dm%s\x1b[0m", color2, color2, buf);
  370. }
  371. else // no ANSI
  372. {
  373. buf[width] = 0;
  374. fputs (buf, stdout);
  375. }
  376. return 0;
  377. }
  378. #if (defined __unix__ && !defined __MSDOS__) || defined __BEOS__ || \
  379. defined __APPLE__ // Mac OS X actually
  380. static int oldtty_set = 0, stdin_tty = 1; // 1 => stdin is a tty, 0 => it's not
  381. static tty_t oldtty, newtty;
  382. void
  383. set_tty (tty_t *param)
  384. {
  385. if (stdin_tty && tcsetattr (STDIN_FILENO, TCSANOW, param) == -1)
  386. {
  387. fprintf (stderr, "ERROR: Could not set tty parameters\n");
  388. exit (100);
  389. }
  390. }
  391. /*
  392. This code compiles with DJGPP, but is not neccesary. Our kbhit() conflicts
  393. with DJGPP's one, so it won't be used for that function. Perhaps it works
  394. for making getchar() behave like getch(), but that's a bit pointless.
  395. */
  396. void
  397. init_conio (void)
  398. {
  399. if (!isatty (STDIN_FILENO))
  400. {
  401. stdin_tty = 0;
  402. return; // rest is nonsense if not a tty
  403. }
  404. if (tcgetattr (STDIN_FILENO, &oldtty) == -1)
  405. {
  406. fprintf (stderr, "ERROR: Could not get tty parameters\n");
  407. exit (101);
  408. }
  409. oldtty_set = 1;
  410. #if 0
  411. if (register_func (deinit_conio) == -1)
  412. {
  413. fprintf (stderr, "ERROR: Could not register function with register_func()\n");
  414. exit (102);
  415. }
  416. #endif
  417. newtty = oldtty;
  418. newtty.c_lflag &= ~(ICANON | ECHO);
  419. newtty.c_lflag |= ISIG;
  420. newtty.c_cc[VMIN] = 1; // if VMIN != 0, read calls
  421. newtty.c_cc[VTIME] = 0; // block (wait for input)
  422. set_tty (&newtty);
  423. }
  424. void
  425. deinit_conio (void)
  426. {
  427. if (oldtty_set)
  428. {
  429. tcsetattr (STDIN_FILENO, TCSAFLUSH, &oldtty);
  430. oldtty_set = 0;
  431. }
  432. }
  433. #if defined __CYGWIN__ && !defined USE_POLL
  434. #warning kbhit() does not work properly in Cygwin executable if USE_POLL is not defined
  435. #endif
  436. // this kbhit() conflicts with DJGPP's one
  437. int
  438. kbhit (void)
  439. {
  440. #ifdef USE_POLL
  441. struct pollfd fd;
  442. fd.fd = STDIN_FILENO;
  443. fd.events = POLLIN;
  444. fd.revents = 0;
  445. return poll (&fd, 1, 0) > 0;
  446. #elif (defined __APPLE__)
  447. struct timeval tv = { 0L, 0L };
  448. fd_set fds;
  449. FD_ZERO(&fds);
  450. FD_SET(0, &fds);
  451. return select(1, &fds, NULL, NULL, &tv);
  452. #else
  453. tty_t tmptty = newtty;
  454. int ch, key_pressed;
  455. tmptty.c_cc[VMIN] = 0; // doesn't work as expected on
  456. set_tty (&tmptty); // Cygwin (define USE_POLL)
  457. if ((ch = fgetc (stdin)) != EOF)
  458. {
  459. key_pressed = 1;
  460. ungetc (ch, stdin);
  461. }
  462. else
  463. key_pressed = 0;
  464. set_tty (&newtty);
  465. return key_pressed;
  466. #endif
  467. }
  468. #elif defined AMIGA // (__unix__ && !__MSDOS__) ||
  469. int // __BEOS__ ||__APPLE__
  470. kbhit (void)
  471. {
  472. return GetKey () != 0xff ? 1 : 0;
  473. }
  474. int
  475. getch (void)
  476. {
  477. BPTR con_fileh;
  478. int temp;
  479. con_fileh = Input ();
  480. // put the console into RAW mode which makes getchar() behave like getch()?
  481. if (con_fileh)
  482. SetMode (con_fileh, 1);
  483. temp = getchar ();
  484. // put the console out of RAW mode (might make sense)
  485. if (con_fileh)
  486. SetMode (con_fileh, 0);
  487. return temp;
  488. }
  489. #endif // AMIGA
  490. //#define TEST
  491. #ifdef TEST
  492. int
  493. main (int argc, char **argv)
  494. {
  495. term_open ();
  496. printf ("%s%s%s%s%s%s", term_up (), term_up (), term_up (), term_up (), term_up (), term_up ());
  497. }
  498. #endif // TEST