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.
 
 
 
 
 
 

601 regels
16 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. It comes without any warranty, to
  9. * the extent permitted by applicable law. You can redistribute it
  10. * and/or modify it under the terms of the Do What The Fuck You Want
  11. * To Public License, Version 2, as published by Sam Hocevar. See
  12. * http://sam.zoy.org/wtfpl/COPYING for more details.
  13. */
  14. /*
  15. * This file contains the libcaca OpenGL input and output driver
  16. */
  17. #include "config.h"
  18. #include "common.h"
  19. #if defined(USE_GL)
  20. #ifdef HAVE_OPENGL_GL_H
  21. # include <OpenGL/gl.h>
  22. # include <GLUT/glut.h>
  23. #else
  24. # include <GL/gl.h>
  25. # include <GL/glut.h>
  26. # include <GL/freeglut_ext.h>
  27. #endif
  28. #include <string.h>
  29. #include <stdlib.h>
  30. #include <stdio.h>
  31. #include "caca.h"
  32. #include "caca_internals.h"
  33. #include "cucul.h"
  34. #include "cucul_internals.h"
  35. /*
  36. * Global variables
  37. */
  38. static caca_display_t *gl_d; /* FIXME: we ought to get rid of this */
  39. /*
  40. * Local functions
  41. */
  42. static void gl_handle_keyboard(unsigned char, int, int);
  43. static void gl_handle_special_key(int, int, int);
  44. static void gl_handle_reshape(int, int);
  45. static void gl_handle_mouse(int, int, int, int);
  46. static void gl_handle_mouse_motion(int, int);
  47. #ifdef HAVE_GLUTCLOSEFUNC
  48. static void gl_handle_close(void);
  49. #endif
  50. static void _display(void);
  51. static void gl_compute_font(caca_display_t *);
  52. struct driver_private
  53. {
  54. int window;
  55. unsigned int width, height;
  56. unsigned int new_width, new_height;
  57. cucul_font_t *f;
  58. float font_width, font_height;
  59. float incx, incy;
  60. unsigned long int const *blocks;
  61. int *txid;
  62. unsigned char close;
  63. unsigned char bit;
  64. unsigned char mouse_changed, mouse_clicked;
  65. unsigned int mouse_x, mouse_y;
  66. unsigned int mouse_button, mouse_state;
  67. unsigned char key;
  68. int special_key;
  69. float sw, sh;
  70. };
  71. static int gl_init_graphics(caca_display_t *dp)
  72. {
  73. char const *geometry;
  74. char *argv[2] = { "", NULL };
  75. char const * const * fonts;
  76. unsigned int width = dp->cv->width, height = dp->cv->height;
  77. int argc = 1;
  78. dp->drv.p = malloc(sizeof(struct driver_private));
  79. gl_d = dp;
  80. #if defined(HAVE_GETENV)
  81. geometry = getenv("CACA_GEOMETRY");
  82. if(geometry && *geometry)
  83. sscanf(geometry, "%ux%u", &width, &height);
  84. #endif
  85. _cucul_set_canvas_size(dp->cv, width ? width : 80, height ? height : 32);
  86. /* Load a libcucul internal font */
  87. fonts = cucul_get_font_list();
  88. if(fonts[0] == NULL)
  89. {
  90. fprintf(stderr, "error: libcucul was compiled without any fonts\n");
  91. return -1;
  92. }
  93. dp->drv.p->f = cucul_load_font(fonts[0], 0);
  94. if(dp->drv.p->f == NULL)
  95. {
  96. fprintf(stderr, "error: could not load font \"%s\"\n", fonts[0]);
  97. return -1;
  98. }
  99. dp->drv.p->font_width = cucul_get_font_width(dp->drv.p->f);
  100. dp->drv.p->font_height = cucul_get_font_height(dp->drv.p->f);
  101. dp->drv.p->width = dp->cv->width * dp->drv.p->font_width;
  102. dp->drv.p->height = dp->cv->height * dp->drv.p->font_height;
  103. #ifdef HAVE_GLUTCLOSEFUNC
  104. dp->drv.p->close = 0;
  105. #endif
  106. dp->drv.p->bit = 0;
  107. dp->drv.p->mouse_changed = dp->drv.p->mouse_clicked = 0;
  108. dp->drv.p->mouse_button = dp->drv.p->mouse_state = 0;
  109. dp->drv.p->key = 0;
  110. dp->drv.p->special_key = 0;
  111. dp->drv.p->sw = ((float)dp->drv.p->font_width) / 16.0f;
  112. dp->drv.p->sh = ((float)dp->drv.p->font_height) / 16.0f;
  113. glutInit(&argc, argv);
  114. glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);
  115. glutInitWindowSize(dp->drv.p->width, dp->drv.p->height);
  116. dp->drv.p->window = glutCreateWindow("caca for GL");
  117. gluOrtho2D(0, dp->drv.p->width, dp->drv.p->height, 0);
  118. glDisable(GL_CULL_FACE);
  119. glDisable(GL_DEPTH_TEST);
  120. glutKeyboardFunc(gl_handle_keyboard);
  121. glutSpecialFunc(gl_handle_special_key);
  122. glutReshapeFunc(gl_handle_reshape);
  123. glutDisplayFunc(_display);
  124. #ifdef HAVE_GLUTCLOSEFUNC
  125. glutCloseFunc(gl_handle_close);
  126. #endif
  127. glutMouseFunc(gl_handle_mouse);
  128. glutMotionFunc(gl_handle_mouse_motion);
  129. glutPassiveMotionFunc(gl_handle_mouse_motion);
  130. glLoadIdentity();
  131. glMatrixMode(GL_PROJECTION);
  132. glPushMatrix();
  133. glLoadIdentity();
  134. gluOrtho2D(0, dp->drv.p->width, dp->drv.p->height, 0);
  135. glMatrixMode(GL_MODELVIEW);
  136. glClear(GL_COLOR_BUFFER_BIT);
  137. glEnable(GL_TEXTURE_2D);
  138. glEnable(GL_BLEND);
  139. glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  140. glEnable(GL_TEXTURE_2D);
  141. gl_compute_font(dp);
  142. return 0;
  143. }
  144. static int gl_end_graphics(caca_display_t *dp)
  145. {
  146. glutDestroyWindow(dp->drv.p->window);
  147. free(dp->drv.p->txid);
  148. free(dp->drv.p);
  149. return 0;
  150. }
  151. static int gl_set_display_title(caca_display_t *dp, char const *title)
  152. {
  153. glutSetWindowTitle(title);
  154. return 0;
  155. }
  156. static unsigned int gl_get_display_width(caca_display_t *dp)
  157. {
  158. return dp->drv.p->width;
  159. }
  160. static unsigned int gl_get_display_height(caca_display_t *dp)
  161. {
  162. return dp->drv.p->height;
  163. }
  164. static void gl_display(caca_display_t *dp)
  165. {
  166. unsigned int x, y, line;
  167. glClear(GL_COLOR_BUFFER_BIT);
  168. glDisable(GL_TEXTURE_2D);
  169. glDisable(GL_BLEND);
  170. line = 0;
  171. for(y = 0; y < dp->drv.p->height; y += dp->drv.p->font_height)
  172. {
  173. uint32_t *attrs = dp->cv->attrs + line * dp->cv->width;
  174. /* FIXME: optimise using stride */
  175. for(x = 0; x < dp->drv.p->width; x += dp->drv.p->font_width)
  176. {
  177. uint16_t bg = _cucul_attr_to_rgb12bg(*attrs++);
  178. glColor4b(((bg & 0xf00) >> 8) * 8,
  179. ((bg & 0x0f0) >> 4) * 8,
  180. (bg & 0x00f) * 8,
  181. 0xff);
  182. glBegin(GL_QUADS);
  183. glVertex2f(x, y);
  184. glVertex2f(x + dp->drv.p->font_width, y);
  185. glVertex2f(x + dp->drv.p->font_width,
  186. y + dp->drv.p->font_height);
  187. glVertex2f(x, y + dp->drv.p->font_height);
  188. glEnd();
  189. }
  190. line++;
  191. }
  192. /* 2nd pass, avoids changing render state too much */
  193. glEnable(GL_TEXTURE_2D);
  194. glEnable(GL_BLEND);
  195. line = 0;
  196. for(y = 0; y < dp->drv.p->height; y += dp->drv.p->font_height, line++)
  197. {
  198. uint32_t *attrs = dp->cv->attrs + line * dp->cv->width;
  199. uint32_t *chars = dp->cv->chars + line * dp->cv->width;
  200. for(x = 0; x < dp->drv.p->width; x += dp->drv.p->font_width, attrs++)
  201. {
  202. uint32_t ch = *chars++;
  203. uint16_t fg;
  204. int i, b, fullwidth;
  205. fullwidth = cucul_utf32_is_fullwidth(ch);
  206. for(b = 0, i = 0; dp->drv.p->blocks[i + 1]; i += 2)
  207. {
  208. if(ch < (uint32_t)dp->drv.p->blocks[i])
  209. break;
  210. if(ch >= (uint32_t)dp->drv.p->blocks[i + 1])
  211. {
  212. b += (uint32_t)(dp->drv.p->blocks[i + 1]
  213. - dp->drv.p->blocks[i]);
  214. continue;
  215. }
  216. glBindTexture(GL_TEXTURE_2D,
  217. dp->drv.p->txid[b + ch
  218. - (uint32_t)dp->drv.p->blocks[i]]);
  219. fg = _cucul_attr_to_rgb12fg(*attrs);
  220. glColor3b(((fg & 0xf00) >> 8) * 8,
  221. ((fg & 0x0f0) >> 4) * 8,
  222. (fg & 0x00f) * 8);
  223. /* FIXME: handle fullwidth glyphs here */
  224. glBegin(GL_QUADS);
  225. glTexCoord2f(0, dp->drv.p->sh);
  226. glVertex2f(x, y);
  227. glTexCoord2f(dp->drv.p->sw, dp->drv.p->sh);
  228. glVertex2f(x + dp->drv.p->font_width, y);
  229. glTexCoord2f(dp->drv.p->sw, 0);
  230. glVertex2f(x + dp->drv.p->font_width,
  231. y + dp->drv.p->font_height);
  232. glTexCoord2f(0, 0);
  233. glVertex2f(x, y + dp->drv.p->font_height);
  234. glEnd();
  235. }
  236. if(fullwidth)
  237. {
  238. chars++; attrs++; x += dp->drv.p->font_width;
  239. }
  240. }
  241. }
  242. #ifdef HAVE_GLUTCHECKLOOP
  243. glutCheckLoop();
  244. #else
  245. glutMainLoopEvent();
  246. #endif
  247. glutSwapBuffers();
  248. glutPostRedisplay();
  249. }
  250. static void gl_handle_resize(caca_display_t *dp)
  251. {
  252. dp->drv.p->width = dp->drv.p->new_width;
  253. dp->drv.p->height = dp->drv.p->new_height;
  254. glMatrixMode(GL_PROJECTION);
  255. glPushMatrix();
  256. glLoadIdentity();
  257. glViewport(0, 0, dp->drv.p->width, dp->drv.p->height);
  258. gluOrtho2D(0, dp->drv.p->width, dp->drv.p->height, 0);
  259. glMatrixMode(GL_MODELVIEW);
  260. }
  261. static int gl_get_event(caca_display_t *dp, caca_event_t *ev)
  262. {
  263. #ifdef HAVE_GLUTCHECKLOOP
  264. glutCheckLoop();
  265. #else
  266. glutMainLoopEvent();
  267. #endif
  268. #ifdef HAVE_GLUTCLOSEFUNC
  269. if(dp->drv.p->close)
  270. {
  271. dp->drv.p->close = 0;
  272. ev->type = CACA_EVENT_QUIT;
  273. return 1;
  274. }
  275. #endif
  276. if(dp->resize.resized)
  277. {
  278. ev->type = CACA_EVENT_RESIZE;
  279. ev->data.resize.w = dp->cv->width;
  280. ev->data.resize.h = dp->cv->height;
  281. return 1;
  282. }
  283. if(dp->drv.p->mouse_changed)
  284. {
  285. ev->type = CACA_EVENT_MOUSE_MOTION;
  286. ev->data.mouse.x = dp->mouse.x;
  287. ev->data.mouse.y = dp->mouse.y;
  288. dp->drv.p->mouse_changed = 0;
  289. if(dp->drv.p->mouse_clicked)
  290. {
  291. _push_event(dp, ev);
  292. ev->type = CACA_EVENT_MOUSE_PRESS;
  293. ev->data.mouse.button = dp->drv.p->mouse_button;
  294. dp->drv.p->mouse_clicked = 0;
  295. }
  296. return 1;
  297. }
  298. if(dp->drv.p->key != 0)
  299. {
  300. ev->type = CACA_EVENT_KEY_PRESS;
  301. ev->data.key.ch = dp->drv.p->key;
  302. ev->data.key.utf32 = (uint32_t)dp->drv.p->key;
  303. ev->data.key.utf8[0] = dp->drv.p->key;
  304. ev->data.key.utf8[1] = '\0';
  305. dp->drv.p->key = 0;
  306. return 1;
  307. }
  308. if(dp->drv.p->special_key != 0)
  309. {
  310. switch(dp->drv.p->special_key)
  311. {
  312. case GLUT_KEY_F1 : ev->data.key.ch = CACA_KEY_F1; break;
  313. case GLUT_KEY_F2 : ev->data.key.ch = CACA_KEY_F2; break;
  314. case GLUT_KEY_F3 : ev->data.key.ch = CACA_KEY_F3; break;
  315. case GLUT_KEY_F4 : ev->data.key.ch = CACA_KEY_F4; break;
  316. case GLUT_KEY_F5 : ev->data.key.ch = CACA_KEY_F5; break;
  317. case GLUT_KEY_F6 : ev->data.key.ch = CACA_KEY_F6; break;
  318. case GLUT_KEY_F7 : ev->data.key.ch = CACA_KEY_F7; break;
  319. case GLUT_KEY_F8 : ev->data.key.ch = CACA_KEY_F8; break;
  320. case GLUT_KEY_F9 : ev->data.key.ch = CACA_KEY_F9; break;
  321. case GLUT_KEY_F10: ev->data.key.ch = CACA_KEY_F10; break;
  322. case GLUT_KEY_F11: ev->data.key.ch = CACA_KEY_F11; break;
  323. case GLUT_KEY_F12: ev->data.key.ch = CACA_KEY_F12; break;
  324. case GLUT_KEY_LEFT : ev->data.key.ch = CACA_KEY_LEFT; break;
  325. case GLUT_KEY_RIGHT: ev->data.key.ch = CACA_KEY_RIGHT; break;
  326. case GLUT_KEY_UP : ev->data.key.ch = CACA_KEY_UP; break;
  327. case GLUT_KEY_DOWN : ev->data.key.ch = CACA_KEY_DOWN; break;
  328. case GLUT_KEY_PAGE_UP : ev->data.key.ch = CACA_KEY_PAGEUP; break;
  329. case GLUT_KEY_PAGE_DOWN : ev->data.key.ch = CACA_KEY_PAGEDOWN;
  330. break;
  331. case GLUT_KEY_HOME : ev->data.key.ch = CACA_KEY_HOME; break;
  332. case GLUT_KEY_END : ev->data.key.ch = CACA_KEY_END; break;
  333. case GLUT_KEY_INSERT : ev->data.key.ch = CACA_KEY_INSERT; break;
  334. default: ev->type = CACA_EVENT_NONE; return 0;
  335. }
  336. ev->type = CACA_EVENT_KEY_PRESS;
  337. ev->data.key.utf32 = 0;
  338. ev->data.key.utf8[0] = '\0';
  339. dp->drv.p->special_key = 0;
  340. return 1;
  341. }
  342. ev->type = CACA_EVENT_NONE;
  343. return 0;
  344. }
  345. static void gl_set_mouse(caca_display_t *dp, int flag)
  346. {
  347. if(flag)
  348. glutSetCursor(GLUT_CURSOR_RIGHT_ARROW);
  349. else
  350. glutSetCursor(GLUT_CURSOR_NONE);
  351. }
  352. /*
  353. * XXX: following functions are local
  354. */
  355. static void gl_handle_keyboard(unsigned char key, int x, int y)
  356. {
  357. caca_display_t *dp = gl_d;
  358. dp->drv.p->key = key;
  359. }
  360. static void gl_handle_special_key(int key, int x, int y)
  361. {
  362. caca_display_t *dp = gl_d;
  363. dp->drv.p->special_key = key;
  364. }
  365. static void gl_handle_reshape(int w, int h)
  366. {
  367. caca_display_t *dp = gl_d;
  368. if(dp->drv.p->bit) /* Do not handle reshaping at the first time */
  369. {
  370. dp->drv.p->new_width = w;
  371. dp->drv.p->new_height = h;
  372. dp->resize.w = w / dp->drv.p->font_width;
  373. dp->resize.h = (h / dp->drv.p->font_height) + 1;
  374. dp->resize.resized = 1;
  375. }
  376. else
  377. dp->drv.p->bit = 1;
  378. }
  379. static void gl_handle_mouse(int button, int state, int x, int y)
  380. {
  381. caca_display_t *dp = gl_d;
  382. dp->drv.p->mouse_clicked = 1;
  383. dp->drv.p->mouse_button = button;
  384. dp->drv.p->mouse_state = state;
  385. dp->drv.p->mouse_x = x / dp->drv.p->font_width;
  386. dp->drv.p->mouse_y = y / dp->drv.p->font_height;
  387. dp->mouse.x = dp->drv.p->mouse_x;
  388. dp->mouse.y = dp->drv.p->mouse_y;
  389. dp->drv.p->mouse_changed = 1;
  390. }
  391. static void gl_handle_mouse_motion(int x, int y)
  392. {
  393. caca_display_t *dp = gl_d;
  394. dp->drv.p->mouse_x = x / dp->drv.p->font_width;
  395. dp->drv.p->mouse_y = y / dp->drv.p->font_height;
  396. dp->mouse.x = dp->drv.p->mouse_x;
  397. dp->mouse.y = dp->drv.p->mouse_y;
  398. dp->drv.p->mouse_changed = 1;
  399. }
  400. #ifdef HAVE_GLUTCLOSEFUNC
  401. static void gl_handle_close(void)
  402. {
  403. caca_display_t *dp = gl_d;
  404. dp->drv.p->close = 1;
  405. }
  406. #endif
  407. static void _display(void)
  408. {
  409. caca_display_t *dp = gl_d;
  410. gl_display(dp);
  411. }
  412. static void gl_compute_font(caca_display_t *dp)
  413. {
  414. cucul_canvas_t *cv;
  415. uint32_t *image;
  416. int i, b, w, h, x, y;
  417. /* Count how many glyphs this font has */
  418. dp->drv.p->blocks = cucul_get_font_blocks(dp->drv.p->f);
  419. for(b = 0, i = 0; dp->drv.p->blocks[i + 1]; i += 2)
  420. b += (int)(dp->drv.p->blocks[i + 1] - dp->drv.p->blocks[i]);
  421. /* Allocate a libcucul canvas and print all the glyphs on it */
  422. cv = cucul_create_canvas(1, b);
  423. cucul_set_color_ansi(cv, CUCUL_WHITE, CUCUL_BLACK);
  424. for(b = 0, i = 0; dp->drv.p->blocks[i + 1]; i += 2)
  425. {
  426. int j, n = (int)(dp->drv.p->blocks[i + 1] - dp->drv.p->blocks[i]);
  427. for(j = 0; j < n; j++)
  428. cucul_put_char(cv, 0, b + j, dp->drv.p->blocks[i] + j);
  429. b += n;
  430. }
  431. /* Draw the cucul canvas onto an image buffer */
  432. image = malloc(b * dp->drv.p->font_height
  433. * dp->drv.p->font_width * sizeof(uint32_t));
  434. cucul_render_canvas(cv, dp->drv.p->f, image, dp->drv.p->font_width,
  435. b * dp->drv.p->font_height, 4 * dp->drv.p->font_width);
  436. cucul_free_canvas(cv);
  437. /* Convert all glyphs in the image buffer to GL textures */
  438. dp->drv.p->txid = malloc(b * sizeof(int));
  439. w = dp->drv.p->font_width <= 16 ? dp->drv.p->font_width : 16;
  440. h = dp->drv.p->font_height <= 16 ? dp->drv.p->font_height : 16;
  441. for(i = 0; i < b; i++)
  442. {
  443. uint8_t tmp[16 * 4 * 16];
  444. uint32_t *glyph = image + (int)(i * dp->drv.p->font_width
  445. * dp->drv.p->font_height);
  446. memset(tmp, 0, 16 * 4 * 16);
  447. for(y = 0; y < h; y++)
  448. {
  449. for(x = 0; x < w; x++)
  450. {
  451. uint32_t offset = x + (15 - y) * 16;
  452. uint8_t c = glyph[x + y * (int)dp->drv.p->font_width] >> 8;
  453. tmp[offset * 4] = c;
  454. tmp[offset * 4 + 1] = c;
  455. tmp[offset * 4 + 2] = c;
  456. tmp[offset * 4 + 3] = c;
  457. }
  458. }
  459. glGenTextures(1, (GLuint*)&dp->drv.p->txid[i]);
  460. glBindTexture(GL_TEXTURE_2D, dp->drv.p->txid[i]);
  461. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  462. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  463. glTexImage2D(GL_TEXTURE_2D, 0, 4, 16, 16, 0,
  464. GL_RGBA, GL_UNSIGNED_BYTE, tmp);
  465. }
  466. free(image);
  467. }
  468. /*
  469. * Driver initialisation
  470. */
  471. int gl_install(caca_display_t *dp)
  472. {
  473. #if defined(HAVE_GETENV) && defined(GLUT_XLIB_IMPLEMENTATION)
  474. if(!getenv("DISPLAY") || !*(getenv("DISPLAY")))
  475. return -1;
  476. #endif
  477. dp->drv.driver = CACA_DRIVER_GL;
  478. dp->drv.init_graphics = gl_init_graphics;
  479. dp->drv.end_graphics = gl_end_graphics;
  480. dp->drv.set_display_title = gl_set_display_title;
  481. dp->drv.get_display_width = gl_get_display_width;
  482. dp->drv.get_display_height = gl_get_display_height;
  483. dp->drv.display = gl_display;
  484. dp->drv.handle_resize = gl_handle_resize;
  485. dp->drv.get_event = gl_get_event;
  486. dp->drv.set_mouse = gl_set_mouse;
  487. dp->drv.set_cursor = NULL;
  488. return 0;
  489. }
  490. #endif /* USE_GL */