You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

427 line
13 KiB

  1. /*
  2. * libcaca ASCII-Art library
  3. * Copyright (c) 2002-2006 Sam Hocevar <sam@zoy.org>
  4. * All Rights Reserved
  5. *
  6. * This library is free software; you can redistribute it and/or
  7. * modify it under the terms of the Do What The Fuck You Want To
  8. * Public License, Version 2, as published by Sam Hocevar. See
  9. * http://sam.zoy.org/wtfpl/COPYING for more details.
  10. */
  11. /** \file driver_ncurses.c
  12. * \version \$Id$
  13. * \author Sam Hocevar <sam@zoy.org>
  14. * \brief Ncurses driver
  15. *
  16. * This file contains the libcaca Ncurses input and output driver
  17. */
  18. #include "config.h"
  19. #if defined(USE_NCURSES)
  20. #if defined(HAVE_NCURSES_H)
  21. # include <ncurses.h>
  22. #else
  23. # include <curses.h>
  24. #endif
  25. #include <stdio.h> /* BUFSIZ */
  26. #include <string.h>
  27. #include <stdlib.h>
  28. #if defined(HAVE_UNISTD_H)
  29. # include <unistd.h>
  30. #endif
  31. #include <stdarg.h>
  32. #if defined(HAVE_SIGNAL_H)
  33. # include <signal.h>
  34. #endif
  35. #if defined(HAVE_SYS_IOCTL_H)
  36. # include <sys/ioctl.h>
  37. #endif
  38. #include "caca.h"
  39. #include "caca_internals.h"
  40. #include "cucul.h"
  41. #include "cucul_internals.h"
  42. /*
  43. * Local functions
  44. */
  45. #if defined(HAVE_SIGNAL)
  46. static RETSIGTYPE sigwinch_handler(int);
  47. static caca_t *sigwinch_kk; /* FIXME: we ought to get rid of this */
  48. #endif
  49. static int ncurses_init_graphics(caca_t *kk)
  50. {
  51. static int curses_colors[] =
  52. {
  53. /* Standard curses colours */
  54. COLOR_BLACK,
  55. COLOR_BLUE,
  56. COLOR_GREEN,
  57. COLOR_CYAN,
  58. COLOR_RED,
  59. COLOR_MAGENTA,
  60. COLOR_YELLOW,
  61. COLOR_WHITE,
  62. /* Extra values for xterm-16color */
  63. COLOR_BLACK + 8,
  64. COLOR_BLUE + 8,
  65. COLOR_GREEN + 8,
  66. COLOR_CYAN + 8,
  67. COLOR_RED + 8,
  68. COLOR_MAGENTA + 8,
  69. COLOR_YELLOW + 8,
  70. COLOR_WHITE + 8
  71. };
  72. mmask_t newmask;
  73. int fg, bg, max;
  74. #if defined(HAVE_SIGNAL)
  75. sigwinch_kk = kk;
  76. signal(SIGWINCH, sigwinch_handler);
  77. #endif
  78. initscr();
  79. keypad(stdscr, TRUE);
  80. nonl();
  81. raw();
  82. noecho();
  83. nodelay(stdscr, TRUE);
  84. curs_set(0);
  85. /* Activate mouse */
  86. newmask = REPORT_MOUSE_POSITION | ALL_MOUSE_EVENTS;
  87. mousemask(newmask, &kk->ncurses.oldmask);
  88. mouseinterval(-1); /* No click emulation */
  89. /* Set the escape delay to a ridiculously low value */
  90. ESCDELAY = 10;
  91. /* Activate colour */
  92. start_color();
  93. /* If COLORS == 16, it means the terminal supports full bright colours
  94. * using setab and setaf (will use \e[90m \e[91m etc. for colours >= 8),
  95. * we can build 16*16 colour pairs.
  96. * If COLORS == 8, it means the terminal does not know about bright
  97. * colours and we need to get them through A_BOLD and A_BLINK (\e[1m
  98. * and \e[5m). We can only build 8*8 colour pairs. */
  99. max = COLORS >= 16 ? 16 : 8;
  100. for(bg = 0; bg < max; bg++)
  101. for(fg = 0; fg < max; fg++)
  102. {
  103. /* Use ((max + 7 - fg) % max) instead of fg so that colour 0
  104. * is light gray on black. Some terminals don't like this
  105. * colour pair to be redefined. */
  106. int col = ((max + 7 - fg) % max) + max * bg;
  107. init_pair(col, curses_colors[fg], curses_colors[bg]);
  108. kk->ncurses.attr[fg + 16 * bg] = COLOR_PAIR(col);
  109. if(max == 8)
  110. {
  111. /* Bright fg on simple bg */
  112. kk->ncurses.attr[fg + 8 + 16 * bg] = A_BOLD | COLOR_PAIR(col);
  113. /* Simple fg on bright bg */
  114. kk->ncurses.attr[fg + 16 * (bg + 8)] = A_BLINK
  115. | COLOR_PAIR(col);
  116. /* Bright fg on bright bg */
  117. kk->ncurses.attr[fg + 8 + 16 * (bg + 8)] = A_BLINK | A_BOLD
  118. | COLOR_PAIR(col);
  119. }
  120. }
  121. cucul_set_size(kk->qq, COLS, LINES);
  122. return 0;
  123. }
  124. static int ncurses_end_graphics(caca_t *kk)
  125. {
  126. mousemask(kk->ncurses.oldmask, NULL);
  127. curs_set(1);
  128. noraw();
  129. endwin();
  130. return 0;
  131. }
  132. static int ncurses_set_window_title(caca_t *kk, char const *title)
  133. {
  134. return 0;
  135. }
  136. static unsigned int ncurses_get_window_width(caca_t *kk)
  137. {
  138. /* Fallback to a 6x10 font */
  139. return kk->qq->width * 6;
  140. }
  141. static unsigned int ncurses_get_window_height(caca_t *kk)
  142. {
  143. /* Fallback to a 6x10 font */
  144. return kk->qq->height * 10;
  145. }
  146. static void ncurses_display(caca_t *kk)
  147. {
  148. int x, y;
  149. uint8_t *attr = kk->qq->attr;
  150. uint32_t *chars = kk->qq->chars;
  151. for(y = 0; y < (int)kk->qq->height; y++)
  152. {
  153. move(y, 0);
  154. for(x = kk->qq->width; x--; )
  155. {
  156. attrset(kk->ncurses.attr[*attr++]);
  157. addch(*chars++ & 0x7f);
  158. }
  159. }
  160. refresh();
  161. }
  162. static void ncurses_handle_resize(caca_t *kk, unsigned int *new_width,
  163. unsigned int *new_height)
  164. {
  165. struct winsize size;
  166. *new_width = kk->qq->width;
  167. *new_height = kk->qq->height;
  168. if(ioctl(fileno(stdout), TIOCGWINSZ, &size) == 0)
  169. {
  170. *new_width = size.ws_col;
  171. *new_height = size.ws_row;
  172. #if defined(HAVE_RESIZE_TERM)
  173. resize_term(*new_height, *new_width);
  174. #else
  175. resizeterm(*new_height, *new_width);
  176. #endif
  177. wrefresh(curscr);
  178. }
  179. }
  180. static unsigned int ncurses_get_event(caca_t *kk)
  181. {
  182. unsigned int event;
  183. int intkey;
  184. if(kk->resize_event)
  185. {
  186. kk->resize_event = 0;
  187. kk->resize = 1;
  188. return CACA_EVENT_RESIZE;
  189. }
  190. intkey = getch();
  191. if(intkey == ERR)
  192. return CACA_EVENT_NONE;
  193. if(intkey < 0x100)
  194. {
  195. return CACA_EVENT_KEY_PRESS | intkey;
  196. }
  197. if(intkey == KEY_MOUSE)
  198. {
  199. MEVENT mevent;
  200. getmouse(&mevent);
  201. switch(mevent.bstate)
  202. {
  203. case BUTTON1_PRESSED:
  204. _push_event(kk, CACA_EVENT_MOUSE_PRESS | 1);
  205. break;
  206. case BUTTON1_RELEASED:
  207. _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 1);
  208. break;
  209. case BUTTON1_CLICKED:
  210. _push_event(kk, CACA_EVENT_MOUSE_PRESS | 1);
  211. _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 1);
  212. break;
  213. case BUTTON1_DOUBLE_CLICKED:
  214. _push_event(kk, CACA_EVENT_MOUSE_PRESS | 1);
  215. _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 1);
  216. _push_event(kk, CACA_EVENT_MOUSE_PRESS | 1);
  217. _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 1);
  218. break;
  219. case BUTTON1_TRIPLE_CLICKED:
  220. _push_event(kk, CACA_EVENT_MOUSE_PRESS | 1);
  221. _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 1);
  222. _push_event(kk, CACA_EVENT_MOUSE_PRESS | 1);
  223. _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 1);
  224. _push_event(kk, CACA_EVENT_MOUSE_PRESS | 1);
  225. _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 1);
  226. break;
  227. case BUTTON1_RESERVED_EVENT:
  228. break;
  229. case BUTTON2_PRESSED:
  230. _push_event(kk, CACA_EVENT_MOUSE_PRESS | 2);
  231. break;
  232. case BUTTON2_RELEASED:
  233. _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 2);
  234. break;
  235. case BUTTON2_CLICKED:
  236. _push_event(kk, CACA_EVENT_MOUSE_PRESS | 2);
  237. _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 2);
  238. break;
  239. case BUTTON2_DOUBLE_CLICKED:
  240. _push_event(kk, CACA_EVENT_MOUSE_PRESS | 2);
  241. _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 2);
  242. _push_event(kk, CACA_EVENT_MOUSE_PRESS | 2);
  243. _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 2);
  244. break;
  245. case BUTTON2_TRIPLE_CLICKED:
  246. _push_event(kk, CACA_EVENT_MOUSE_PRESS | 2);
  247. _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 2);
  248. _push_event(kk, CACA_EVENT_MOUSE_PRESS | 2);
  249. _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 2);
  250. _push_event(kk, CACA_EVENT_MOUSE_PRESS | 2);
  251. _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 2);
  252. break;
  253. case BUTTON2_RESERVED_EVENT:
  254. break;
  255. case BUTTON3_PRESSED:
  256. _push_event(kk, CACA_EVENT_MOUSE_PRESS | 3);
  257. break;
  258. case BUTTON3_RELEASED:
  259. _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 3);
  260. break;
  261. case BUTTON3_CLICKED:
  262. _push_event(kk, CACA_EVENT_MOUSE_PRESS | 3);
  263. _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 3);
  264. break;
  265. case BUTTON3_DOUBLE_CLICKED:
  266. _push_event(kk, CACA_EVENT_MOUSE_PRESS | 3);
  267. _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 3);
  268. _push_event(kk, CACA_EVENT_MOUSE_PRESS | 3);
  269. _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 3);
  270. break;
  271. case BUTTON3_TRIPLE_CLICKED:
  272. _push_event(kk, CACA_EVENT_MOUSE_PRESS | 3);
  273. _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 3);
  274. _push_event(kk, CACA_EVENT_MOUSE_PRESS | 3);
  275. _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 3);
  276. _push_event(kk, CACA_EVENT_MOUSE_PRESS | 3);
  277. _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 3);
  278. break;
  279. case BUTTON3_RESERVED_EVENT:
  280. break;
  281. case BUTTON4_PRESSED:
  282. _push_event(kk, CACA_EVENT_MOUSE_PRESS | 4);
  283. break;
  284. case BUTTON4_RELEASED:
  285. _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 4);
  286. break;
  287. case BUTTON4_CLICKED:
  288. _push_event(kk, CACA_EVENT_MOUSE_PRESS | 4);
  289. _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 4);
  290. break;
  291. case BUTTON4_DOUBLE_CLICKED:
  292. _push_event(kk, CACA_EVENT_MOUSE_PRESS | 4);
  293. _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 4);
  294. _push_event(kk, CACA_EVENT_MOUSE_PRESS | 4);
  295. _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 4);
  296. break;
  297. case BUTTON4_TRIPLE_CLICKED:
  298. _push_event(kk, CACA_EVENT_MOUSE_PRESS | 4);
  299. _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 4);
  300. _push_event(kk, CACA_EVENT_MOUSE_PRESS | 4);
  301. _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 4);
  302. _push_event(kk, CACA_EVENT_MOUSE_PRESS | 4);
  303. _push_event(kk, CACA_EVENT_MOUSE_RELEASE | 4);
  304. break;
  305. case BUTTON4_RESERVED_EVENT:
  306. break;
  307. default:
  308. break;
  309. }
  310. if(kk->mouse_x == (unsigned int)mevent.x &&
  311. kk->mouse_y == (unsigned int)mevent.y)
  312. return _pop_event(kk);
  313. kk->mouse_x = mevent.x;
  314. kk->mouse_y = mevent.y;
  315. return CACA_EVENT_MOUSE_MOTION | (kk->mouse_x << 12) | kk->mouse_y;
  316. }
  317. event = CACA_EVENT_KEY_PRESS;
  318. switch(intkey)
  319. {
  320. case KEY_UP: return event | CACA_KEY_UP;
  321. case KEY_DOWN: return event | CACA_KEY_DOWN;
  322. case KEY_LEFT: return event | CACA_KEY_LEFT;
  323. case KEY_RIGHT: return event | CACA_KEY_RIGHT;
  324. case KEY_IC: return event | CACA_KEY_INSERT;
  325. case KEY_DC: return event | CACA_KEY_DELETE;
  326. case KEY_HOME: return event | CACA_KEY_HOME;
  327. case KEY_END: return event | CACA_KEY_END;
  328. case KEY_PPAGE: return event | CACA_KEY_PAGEUP;
  329. case KEY_NPAGE: return event | CACA_KEY_PAGEDOWN;
  330. case KEY_F(1): return event | CACA_KEY_F1;
  331. case KEY_F(2): return event | CACA_KEY_F2;
  332. case KEY_F(3): return event | CACA_KEY_F3;
  333. case KEY_F(4): return event | CACA_KEY_F4;
  334. case KEY_F(5): return event | CACA_KEY_F5;
  335. case KEY_F(6): return event | CACA_KEY_F6;
  336. case KEY_F(7): return event | CACA_KEY_F7;
  337. case KEY_F(8): return event | CACA_KEY_F8;
  338. case KEY_F(9): return event | CACA_KEY_F9;
  339. case KEY_F(10): return event | CACA_KEY_F10;
  340. case KEY_F(11): return event | CACA_KEY_F11;
  341. case KEY_F(12): return event | CACA_KEY_F12;
  342. }
  343. return CACA_EVENT_NONE;
  344. }
  345. /*
  346. * XXX: following functions are local
  347. */
  348. #if defined(HAVE_SIGNAL)
  349. static RETSIGTYPE sigwinch_handler(int sig)
  350. {
  351. sigwinch_kk->resize_event = 1;
  352. signal(SIGWINCH, sigwinch_handler);;
  353. }
  354. #endif
  355. /*
  356. * Driver initialisation
  357. */
  358. void ncurses_init_driver(caca_t *kk)
  359. {
  360. kk->driver.driver = CACA_DRIVER_NCURSES;
  361. kk->driver.init_graphics = ncurses_init_graphics;
  362. kk->driver.end_graphics = ncurses_end_graphics;
  363. kk->driver.set_window_title = ncurses_set_window_title;
  364. kk->driver.get_window_width = ncurses_get_window_width;
  365. kk->driver.get_window_height = ncurses_get_window_height;
  366. kk->driver.display = ncurses_display;
  367. kk->driver.handle_resize = ncurses_handle_resize;
  368. kk->driver.get_event = ncurses_get_event;
  369. }
  370. #endif /* USE_NCURSES */