Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.
 
 
 
 
 
 

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