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.
 
 
 
 
 
 

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