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.
 
 
 
 
 
 

518 lines
13 KiB

  1. /*
  2. * libcaca Colour ASCII-Art library
  3. * Copyright (c) 2002-2006 Sam Hocevar <sam@zoy.org>
  4. * All Rights Reserved
  5. *
  6. * $Id$
  7. *
  8. * This library 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. /*
  14. * This file contains the libcaca OpenGL input and output driver
  15. */
  16. #include "config.h"
  17. #include "common.h"
  18. #if defined(USE_GL)
  19. #ifdef HAVE_OPENGL_GL_H
  20. # include <OpenGL/gl.h>
  21. # include <GLUT/glut.h>
  22. #else
  23. # include <GL/gl.h>
  24. # include <GL/glut.h>
  25. # include <GL/freeglut_ext.h>
  26. #endif
  27. #include <string.h>
  28. #include <stdlib.h>
  29. #include <stdio.h>
  30. #include "caca.h"
  31. #include "caca_internals.h"
  32. #include "cucul.h"
  33. #include "cucul_internals.h"
  34. /*
  35. * Global variables
  36. */
  37. static caca_display_t *gl_d; /* FIXME: we ought to get rid of this */
  38. /*
  39. * Local functions
  40. */
  41. static void gl_handle_keyboard(unsigned char, int, int);
  42. static void gl_handle_special_key(int, int, int);
  43. static void gl_handle_reshape(int, int);
  44. static void gl_handle_mouse(int, int, int, int);
  45. static void gl_handle_mouse_motion(int, int);
  46. #ifdef HAVE_GLUTCLOSEFUNC
  47. static void gl_handle_close(void);
  48. #endif
  49. static void _display(void);
  50. struct driver_private
  51. {
  52. int window;
  53. unsigned int width, height;
  54. unsigned int new_width, new_height;
  55. float font_width, font_height;
  56. float incx, incy;
  57. int id[128 - 32];
  58. unsigned char close;
  59. unsigned char bit;
  60. unsigned char mouse_changed, mouse_clicked;
  61. unsigned int mouse_x, mouse_y;
  62. unsigned int mouse_button, mouse_state;
  63. unsigned char key;
  64. int special_key;
  65. float sw, sh;
  66. };
  67. static int gl_init_graphics(caca_display_t *dp)
  68. {
  69. char *empty_texture;
  70. char const *geometry;
  71. char *argv[2] = { "", NULL };
  72. unsigned int width = 0, height = 0;
  73. int argc = 1;
  74. int i;
  75. dp->drv.p = malloc(sizeof(struct driver_private));
  76. gl_d = dp;
  77. #if defined(HAVE_GETENV)
  78. geometry = getenv("CACA_GEOMETRY");
  79. if(geometry && *geometry)
  80. sscanf(geometry, "%ux%u", &width, &height);
  81. #endif
  82. if(width && height)
  83. _cucul_set_canvas_size(dp->cv, width, height);
  84. dp->drv.p->font_width = 9;
  85. dp->drv.p->font_height = 15;
  86. dp->drv.p->width = dp->cv->width * dp->drv.p->font_width;
  87. dp->drv.p->height = dp->cv->height * dp->drv.p->font_height;
  88. #ifdef HAVE_GLUTCLOSEFUNC
  89. dp->drv.p->close = 0;
  90. #endif
  91. dp->drv.p->bit = 0;
  92. dp->drv.p->mouse_changed = dp->drv.p->mouse_clicked = 0;
  93. dp->drv.p->mouse_button = dp->drv.p->mouse_state = 0;
  94. dp->drv.p->key = 0;
  95. dp->drv.p->special_key = 0;
  96. dp->drv.p->sw = 9.0f / 16.0f;
  97. dp->drv.p->sh = 15.0f / 16.0f;
  98. glutInit(&argc, argv);
  99. glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE);
  100. glutInitWindowSize(dp->drv.p->width, dp->drv.p->height);
  101. dp->drv.p->window = glutCreateWindow("caca for GL");
  102. gluOrtho2D(0, dp->drv.p->width, dp->drv.p->height, 0);
  103. glDisable(GL_CULL_FACE);
  104. glDisable(GL_DEPTH_TEST);
  105. glutKeyboardFunc(gl_handle_keyboard);
  106. glutSpecialFunc(gl_handle_special_key);
  107. glutReshapeFunc(gl_handle_reshape);
  108. glutDisplayFunc(_display);
  109. #ifdef HAVE_GLUTCLOSEFUNC
  110. glutCloseFunc(gl_handle_close);
  111. #endif
  112. glutMouseFunc(gl_handle_mouse);
  113. glutMotionFunc(gl_handle_mouse_motion);
  114. glutPassiveMotionFunc(gl_handle_mouse_motion);
  115. glLoadIdentity();
  116. glMatrixMode(GL_PROJECTION);
  117. glPushMatrix();
  118. glLoadIdentity();
  119. gluOrtho2D(0, dp->drv.p->width, dp->drv.p->height, 0);
  120. glMatrixMode(GL_MODELVIEW);
  121. glClear(GL_COLOR_BUFFER_BIT);
  122. empty_texture = malloc(16 * 16 * 4);
  123. if(empty_texture == NULL)
  124. return -1;
  125. memset(empty_texture, 0xff, 16 * 16 * 4);
  126. glEnable(GL_TEXTURE_2D);
  127. for(i = 32; i < 128; i++)
  128. {
  129. glGenTextures(1, (GLuint*)&dp->drv.p->id[i - 32]);
  130. glBindTexture(GL_TEXTURE_2D, dp->drv.p->id[i - 32]);
  131. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  132. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  133. glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8,
  134. 16, 16, 0, GL_RGB, GL_UNSIGNED_BYTE, empty_texture);
  135. }
  136. for(i = 32; i < 128; i++)
  137. {
  138. glDisable(GL_TEXTURE_2D);
  139. glClear(GL_COLOR_BUFFER_BIT);
  140. glColor3f(1, 1, 1);
  141. glRasterPos2f(0, 15);
  142. glutBitmapCharacter(GLUT_BITMAP_9_BY_15, i);
  143. glEnable(GL_TEXTURE_2D);
  144. glBindTexture(GL_TEXTURE_2D, dp->drv.p->id[i - 32]);
  145. glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGB,
  146. 0, dp->drv.p->height - 16, 16, 16, 0);
  147. #ifdef HAVE_GLUTCHECKLOOP
  148. glutCheckLoop();
  149. #else
  150. glutMainLoopEvent();
  151. #endif
  152. glutPostRedisplay();
  153. }
  154. return 0;
  155. }
  156. static int gl_end_graphics(caca_display_t *dp)
  157. {
  158. glutDestroyWindow(dp->drv.p->window);
  159. free(dp->drv.p);
  160. return 0;
  161. }
  162. static int gl_set_display_title(caca_display_t *dp, char const *title)
  163. {
  164. glutSetWindowTitle(title);
  165. return 0;
  166. }
  167. static unsigned int gl_get_display_width(caca_display_t *dp)
  168. {
  169. return dp->drv.p->width;
  170. }
  171. static unsigned int gl_get_display_height(caca_display_t *dp)
  172. {
  173. return dp->drv.p->height;
  174. }
  175. static void gl_display(caca_display_t *dp)
  176. {
  177. unsigned int x, y, line;
  178. glClear(GL_COLOR_BUFFER_BIT);
  179. line = 0;
  180. for(y = 0; y < dp->drv.p->height; y += dp->drv.p->font_height)
  181. {
  182. uint32_t *attr = dp->cv->attr + line * dp->cv->width;
  183. for(x = 0; x < dp->drv.p->width; x += dp->drv.p->font_width)
  184. {
  185. uint16_t bg = _cucul_argb32_to_rgb12bg(*attr++);
  186. glDisable(GL_TEXTURE_2D);
  187. glColor3b(((bg & 0xf00) >> 8) * 8,
  188. ((bg & 0x0f0) >> 4) * 8,
  189. (bg & 0x00f) * 8);
  190. glBegin(GL_QUADS);
  191. glVertex2f(x, y);
  192. glVertex2f(x + dp->drv.p->font_width, y);
  193. glVertex2f(x + dp->drv.p->font_width,
  194. y + dp->drv.p->font_height);
  195. glVertex2f(x, y + dp->drv.p->font_height);
  196. glEnd();
  197. }
  198. line++;
  199. }
  200. /* 2nd pass, avoids changing render state too much */
  201. glEnable(GL_BLEND);
  202. glEnable(GL_TEXTURE_2D);
  203. glBlendFunc(GL_ONE, GL_ONE);
  204. line = 0;
  205. for(y = 0; y < dp->drv.p->height; y += dp->drv.p->font_height)
  206. {
  207. uint32_t *attr = dp->cv->attr + line * dp->cv->width;
  208. uint32_t *chars = dp->cv->chars + line * dp->cv->width;
  209. for(x = 0; x < dp->drv.p->width; x += dp->drv.p->font_width)
  210. {
  211. uint32_t cv = *chars++;
  212. if(cv > 0x00000020 && cv < 0x00000080)
  213. {
  214. uint16_t fg = _cucul_argb32_to_rgb12fg(*attr);
  215. glBindTexture(GL_TEXTURE_2D, dp->drv.p->id[cv - 32]);
  216. glColor3b(((fg & 0xf00) >> 8) * 8,
  217. ((fg & 0x0f0) >> 4) * 8,
  218. (fg & 0x00f) * 8);
  219. glBegin(GL_QUADS);
  220. glTexCoord2f(0, dp->drv.p->sh);
  221. glVertex2f(x, y);
  222. glTexCoord2f(dp->drv.p->sw, dp->drv.p->sh);
  223. glVertex2f(x + dp->drv.p->font_width, y);
  224. glTexCoord2f(dp->drv.p->sw, 0);
  225. glVertex2f(x + dp->drv.p->font_width,
  226. y + dp->drv.p->font_height);
  227. glTexCoord2f(0, 0);
  228. glVertex2f(x, y + dp->drv.p->font_height);
  229. glEnd();
  230. }
  231. attr++;
  232. }
  233. line++;
  234. }
  235. glDisable(GL_BLEND);
  236. glDisable(GL_TEXTURE_2D);
  237. #ifdef HAVE_GLUTCHECKLOOP
  238. glutCheckLoop();
  239. #else
  240. glutMainLoopEvent();
  241. #endif
  242. glutSwapBuffers();
  243. glutPostRedisplay();
  244. }
  245. static void gl_handle_resize(caca_display_t *dp)
  246. {
  247. dp->drv.p->width = dp->drv.p->new_width;
  248. dp->drv.p->height = dp->drv.p->new_height;
  249. glMatrixMode(GL_PROJECTION);
  250. glPushMatrix();
  251. glLoadIdentity();
  252. glViewport(0, 0, dp->drv.p->width, dp->drv.p->height);
  253. gluOrtho2D(0, dp->drv.p->width, dp->drv.p->height, 0);
  254. glMatrixMode(GL_MODELVIEW);
  255. }
  256. static int gl_get_event(caca_display_t *dp, caca_event_t *ev)
  257. {
  258. #ifdef HAVE_GLUTCHECKLOOP
  259. glutCheckLoop();
  260. #else
  261. glutMainLoopEvent();
  262. #endif
  263. #ifdef HAVE_GLUTCLOSEFUNC
  264. if(dp->drv.p->close)
  265. {
  266. dp->drv.p->close = 0;
  267. ev->type = CACA_EVENT_QUIT;
  268. return 1;
  269. }
  270. #endif
  271. if(dp->resize.resized)
  272. {
  273. ev->type = CACA_EVENT_RESIZE;
  274. ev->data.resize.w = dp->cv->width;
  275. ev->data.resize.h = dp->cv->height;
  276. return 1;
  277. }
  278. if(dp->drv.p->mouse_changed)
  279. {
  280. ev->type = CACA_EVENT_MOUSE_MOTION;
  281. ev->data.mouse.x = dp->mouse.x;
  282. ev->data.mouse.y = dp->mouse.y;
  283. dp->drv.p->mouse_changed = 0;
  284. if(dp->drv.p->mouse_clicked)
  285. {
  286. _push_event(dp, ev);
  287. ev->type = CACA_EVENT_MOUSE_PRESS;
  288. ev->data.mouse.button = dp->drv.p->mouse_button;
  289. dp->drv.p->mouse_clicked = 0;
  290. }
  291. return 1;
  292. }
  293. if(dp->drv.p->key != 0)
  294. {
  295. ev->type = CACA_EVENT_KEY_PRESS;
  296. ev->data.key.ch = dp->drv.p->key;
  297. ev->data.key.ucs4 = (uint32_t)dp->drv.p->key;
  298. ev->data.key.utf8[0] = dp->drv.p->key;
  299. ev->data.key.utf8[1] = '\0';
  300. dp->drv.p->key = 0;
  301. return 1;
  302. }
  303. if(dp->drv.p->special_key != 0)
  304. {
  305. switch(dp->drv.p->special_key)
  306. {
  307. case GLUT_KEY_F1 : ev->data.key.ch = CACA_KEY_F1; break;
  308. case GLUT_KEY_F2 : ev->data.key.ch = CACA_KEY_F2; break;
  309. case GLUT_KEY_F3 : ev->data.key.ch = CACA_KEY_F3; break;
  310. case GLUT_KEY_F4 : ev->data.key.ch = CACA_KEY_F4; break;
  311. case GLUT_KEY_F5 : ev->data.key.ch = CACA_KEY_F5; break;
  312. case GLUT_KEY_F6 : ev->data.key.ch = CACA_KEY_F6; break;
  313. case GLUT_KEY_F7 : ev->data.key.ch = CACA_KEY_F7; break;
  314. case GLUT_KEY_F8 : ev->data.key.ch = CACA_KEY_F8; break;
  315. case GLUT_KEY_F9 : ev->data.key.ch = CACA_KEY_F9; break;
  316. case GLUT_KEY_F10: ev->data.key.ch = CACA_KEY_F10; break;
  317. case GLUT_KEY_F11: ev->data.key.ch = CACA_KEY_F11; break;
  318. case GLUT_KEY_F12: ev->data.key.ch = CACA_KEY_F12; break;
  319. case GLUT_KEY_LEFT : ev->data.key.ch = CACA_KEY_LEFT; break;
  320. case GLUT_KEY_RIGHT: ev->data.key.ch = CACA_KEY_RIGHT; break;
  321. case GLUT_KEY_UP : ev->data.key.ch = CACA_KEY_UP; break;
  322. case GLUT_KEY_DOWN : ev->data.key.ch = CACA_KEY_DOWN; break;
  323. default: ev->type = CACA_EVENT_NONE; return 0;
  324. }
  325. ev->type = CACA_EVENT_KEY_PRESS;
  326. ev->data.key.ucs4 = 0;
  327. ev->data.key.utf8[0] = '\0';
  328. dp->drv.p->special_key = 0;
  329. return 1;
  330. }
  331. ev->type = CACA_EVENT_NONE;
  332. return 0;
  333. }
  334. static void gl_set_mouse(caca_display_t *dp, int flag)
  335. {
  336. if(flag)
  337. glutSetCursor(GLUT_CURSOR_RIGHT_ARROW);
  338. else
  339. glutSetCursor(GLUT_CURSOR_NONE);
  340. }
  341. /*
  342. * XXX: following functions are local
  343. */
  344. static void gl_handle_keyboard(unsigned char key, int x, int y)
  345. {
  346. caca_display_t *dp = gl_d;
  347. dp->drv.p->key = key;
  348. }
  349. static void gl_handle_special_key(int key, int x, int y)
  350. {
  351. caca_display_t *dp = gl_d;
  352. dp->drv.p->special_key = key;
  353. }
  354. static void gl_handle_reshape(int w, int h)
  355. {
  356. caca_display_t *dp = gl_d;
  357. if(dp->drv.p->bit) /* Do not handle reshaping at the first time */
  358. {
  359. dp->drv.p->new_width = w;
  360. dp->drv.p->new_height = h;
  361. dp->resize.w = w / dp->drv.p->font_width;
  362. dp->resize.h = (h / dp->drv.p->font_height) + 1;
  363. dp->resize.resized = 1;
  364. }
  365. else
  366. dp->drv.p->bit = 1;
  367. }
  368. static void gl_handle_mouse(int button, int state, int x, int y)
  369. {
  370. caca_display_t *dp = gl_d;
  371. dp->drv.p->mouse_clicked = 1;
  372. dp->drv.p->mouse_button = button;
  373. dp->drv.p->mouse_state = state;
  374. dp->drv.p->mouse_x = x / dp->drv.p->font_width;
  375. dp->drv.p->mouse_y = y / dp->drv.p->font_height;
  376. dp->mouse.x = dp->drv.p->mouse_x;
  377. dp->mouse.y = dp->drv.p->mouse_y;
  378. dp->drv.p->mouse_changed = 1;
  379. }
  380. static void gl_handle_mouse_motion(int x, int y)
  381. {
  382. caca_display_t *dp = gl_d;
  383. dp->drv.p->mouse_x = x / dp->drv.p->font_width;
  384. dp->drv.p->mouse_y = y / dp->drv.p->font_height;
  385. dp->mouse.x = dp->drv.p->mouse_x;
  386. dp->mouse.y = dp->drv.p->mouse_y;
  387. dp->drv.p->mouse_changed = 1;
  388. }
  389. #ifdef HAVE_GLUTCLOSEFUNC
  390. static void gl_handle_close(void)
  391. {
  392. caca_display_t *dp = gl_d;
  393. dp->drv.p->close = 1;
  394. }
  395. #endif
  396. static void _display(void)
  397. {
  398. caca_display_t *dp = gl_d;
  399. gl_display(dp);
  400. }
  401. /*
  402. * Driver initialisation
  403. */
  404. int gl_install(caca_display_t *dp)
  405. {
  406. #if defined(HAVE_GETENV) && defined(GLUT_XLIB_IMPLEMENTATION)
  407. if(!getenv("DISPLAY") || !*(getenv("DISPLAY")))
  408. return -1;
  409. #endif
  410. dp->drv.driver = CACA_DRIVER_GL;
  411. dp->drv.init_graphics = gl_init_graphics;
  412. dp->drv.end_graphics = gl_end_graphics;
  413. dp->drv.set_display_title = gl_set_display_title;
  414. dp->drv.get_display_width = gl_get_display_width;
  415. dp->drv.get_display_height = gl_get_display_height;
  416. dp->drv.display = gl_display;
  417. dp->drv.handle_resize = gl_handle_resize;
  418. dp->drv.get_event = gl_get_event;
  419. dp->drv.set_mouse = gl_set_mouse;
  420. return 0;
  421. }
  422. #endif /* USE_GL */