source: libcaca/trunk/caca/driver_gl.c @ 906

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