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

Last change on this file since 684 was 684, checked in by Sam Hocevar, 15 years ago
  • Allow the driver initialisation to fail, for instance when $DISPLAY = "".
  • Property svn:keywords set to Id
File size: 12.2 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 *  This library is free software; you can redistribute it and/or
7 *  modify it under the terms of the Do What The Fuck You Want To
8 *  Public License, Version 2, as published by Sam Hocevar. See
9 *  http://sam.zoy.org/wtfpl/COPYING for more details.
10 */
11
12/** \file driver_gl.c
13 *  \version \$Id: driver_gl.c 684 2006-03-24 09:48:20Z sam $
14 *  \author Jean-Yves Lamoureux <jylam@lnxscene.org>
15 *  \brief OpenGL driver
16 *
17 *  This file contains the libcaca OpenGL input and output driver
18 */
19
20#include "config.h"
21
22#if defined(USE_GL)
23
24#include <GL/gl.h>
25#include <GL/glut.h>
26#include <GL/freeglut_ext.h>
27
28#include <string.h>
29#include <stdlib.h>
30#include <stdio.h>
31
32#include "caca.h"
33#include "caca_internals.h"
34#include "cucul.h"
35#include "cucul_internals.h"
36
37/*
38 * Global variables
39 */
40
41/* Ok, I just suck. */
42static GLbyte const gl_bgpal[][4] =
43{
44    { 0x00, 0x00, 0x00, 0x7f },
45    { 0x00, 0x00, 0x3f, 0x7f },
46    { 0x00, 0x3f, 0x00, 0x7f },
47    { 0x00, 0x3f, 0x3f, 0x7f },
48    { 0x3f, 0x00, 0x00, 0x7f },
49    { 0x3f, 0x00, 0x3f, 0x7f },
50    { 0x3f, 0x3f, 0x00, 0x7f },
51    { 0x3f, 0x3f, 0x3f, 0x7f },
52    // + intensity
53    // >.
54    // ()
55    // ^^
56    { 0x1f, 0x1f, 0x1f, 0x7f },
57    { 0x1f, 0x1f, 0x7f, 0x7f },
58    { 0x1f, 0x7f, 0x1f, 0x7f },
59    { 0x1f, 0x7f, 0x7f, 0x7f },
60    { 0x7f, 0x1f, 0x1f, 0x7f },
61    { 0x7f, 0x1f, 0x7f, 0x7f },
62    { 0x7f, 0x7f, 0x1f, 0x7f },
63    { 0x7f, 0x7f, 0x7f, 0x7f }
64};
65
66static caca_t *gl_kk; /* FIXME: we ought to get rid of this */
67
68/*
69 * Local functions
70 */
71static void gl_handle_keyboard(unsigned char, int, int);
72static void gl_handle_special_key(int, int, int);
73static void gl_handle_reshape(int, int);
74static void gl_handle_mouse(int, int, int, int);
75static void gl_handle_mouse_motion(int, int);
76
77struct driver_private
78{
79    int window;
80    unsigned int width, height;
81    unsigned int new_width, new_height;
82    float font_width, font_height;
83    float incx, incy;
84    int id[128 - 32];
85    unsigned char bit;
86    unsigned char mouse_changed, mouse_clicked;
87    unsigned int mouse_x, mouse_y;
88    unsigned int mouse_button, mouse_state;
89
90    unsigned char key;
91    int special_key;
92
93    float sw, sh;
94};
95
96static int gl_init_graphics(caca_t *kk)
97{
98    char *empty_texture;
99    char const *geometry;
100    char *argv[2] = { "", NULL };
101    unsigned int width = 0, height = 0;
102    int argc = 1;
103    int i;
104
105    kk->drv.p = malloc(sizeof(struct driver_private));
106
107    gl_kk = kk;
108
109#if defined(HAVE_GETENV)
110    geometry = getenv("CACA_GEOMETRY");
111    if(geometry && *geometry)
112        sscanf(geometry, "%ux%u", &width, &height);
113#endif
114
115    if(width && height)
116        _cucul_set_size(kk->qq, width, height);
117
118    kk->drv.p->font_width = 9;
119    kk->drv.p->font_height = 15;
120
121    kk->drv.p->width = kk->qq->width * kk->drv.p->font_width;
122    kk->drv.p->height = kk->qq->height * kk->drv.p->font_height;
123
124    kk->drv.p->bit = 0;
125
126    kk->drv.p->mouse_changed = kk->drv.p->mouse_clicked = 0;
127    kk->drv.p->mouse_button = kk->drv.p->mouse_state = 0;
128
129    kk->drv.p->key = 0;
130    kk->drv.p->special_key = 0;
131
132    kk->drv.p->sw = 9.0f / 16.0f;
133    kk->drv.p->sh = 15.0f / 16.0f;
134
135    glutInit(&argc, argv);
136
137    glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE);
138    glutInitWindowSize(kk->drv.p->width, kk->drv.p->height);
139    kk->drv.p->window = glutCreateWindow("caca for GL");
140
141    gluOrtho2D(0, kk->drv.p->width, kk->drv.p->height, 0);
142
143    glDisable(GL_CULL_FACE);
144    glDisable(GL_DEPTH_TEST);
145
146    glutKeyboardFunc(gl_handle_keyboard);
147    glutSpecialFunc(gl_handle_special_key);
148    glutReshapeFunc(gl_handle_reshape);
149
150    glutMouseFunc(gl_handle_mouse);
151    glutMotionFunc(gl_handle_mouse_motion);
152    glutPassiveMotionFunc(gl_handle_mouse_motion);
153
154    glLoadIdentity();
155
156    glMatrixMode(GL_PROJECTION);
157    glPushMatrix();
158    glLoadIdentity();
159    gluOrtho2D(0, kk->drv.p->width, kk->drv.p->height, 0);
160
161    glMatrixMode(GL_MODELVIEW);
162
163    glClear(GL_COLOR_BUFFER_BIT);
164
165    empty_texture = malloc(16 * 16 * 4);
166    if(empty_texture == NULL)
167        return -1;
168
169    memset(empty_texture, 0xff, 16 * 16 * 4);
170    glEnable(GL_TEXTURE_2D);
171
172    for(i = 32; i < 128; i++)
173    {
174        glGenTextures(1, (GLuint*)&kk->drv.p->id[i - 32]);
175        glBindTexture(GL_TEXTURE_2D, kk->drv.p->id[i - 32]);
176        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
177        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
178        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8,
179                     16, 16, 0, GL_RGB, GL_UNSIGNED_BYTE, empty_texture);
180    }
181
182    for(i = 32; i < 128; i++)
183    {
184        glDisable(GL_TEXTURE_2D);
185        glClear(GL_COLOR_BUFFER_BIT);
186
187        glColor3f(1, 1, 1);
188        glRasterPos2f(0, 15);
189        glutBitmapCharacter(GLUT_BITMAP_9_BY_15, i);
190
191        glEnable(GL_TEXTURE_2D);
192        glBindTexture(GL_TEXTURE_2D, kk->drv.p->id[i - 32]);
193        glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGB,
194                         0, kk->drv.p->height - 16, 16, 16, 0);
195
196        glutMainLoopEvent();
197        glutPostRedisplay();
198    }
199
200    return 0;
201}
202
203static int gl_end_graphics(caca_t *kk)
204{
205    glutDestroyWindow(kk->drv.p->window);
206    free(kk->drv.p);
207    return 0;
208}
209
210static int gl_set_window_title(caca_t *kk, char const *title)
211{
212    glutSetWindowTitle(title);
213    return 0;
214}
215
216static unsigned int gl_get_window_width(caca_t *kk)
217{
218    return kk->drv.p->width;
219}
220
221static unsigned int gl_get_window_height(caca_t *kk)
222{
223    return kk->drv.p->height;
224}
225
226static void gl_display(caca_t *kk)
227{
228    unsigned int x, y, line;
229
230    glClear(GL_COLOR_BUFFER_BIT);
231
232    line = 0;
233    for(y = 0; y < kk->drv.p->height; y += kk->drv.p->font_height)
234    {
235        uint8_t *attr = kk->qq->attr + line * kk->qq->width;
236
237        for(x = 0; x < kk->drv.p->width; x += kk->drv.p->font_width)
238        {
239            glDisable(GL_TEXTURE_2D);
240            glColor4bv(gl_bgpal[attr[0] >> 4]);
241            glBegin(GL_QUADS);
242                glVertex2f(x, y);
243                glVertex2f(x + kk->drv.p->font_width, y);
244                glVertex2f(x + kk->drv.p->font_width,
245                           y + kk->drv.p->font_height);
246                glVertex2f(x, y + kk->drv.p->font_height);
247            glEnd();
248
249            attr++;
250        }
251
252        line++;
253    }
254
255    /* 2nd pass, avoids changing render state too much */
256    glEnable(GL_BLEND);
257    glEnable(GL_TEXTURE_2D);
258    glBlendFunc(GL_ONE, GL_ONE);
259
260    line = 0;
261    for(y = 0; y < kk->drv.p->height; y += kk->drv.p->font_height)
262    {
263        uint8_t *attr = kk->qq->attr + line * kk->qq->width;
264        uint32_t *chars = kk->qq->chars + line * kk->qq->width;
265
266        for(x = 0; x < kk->drv.p->width; x += kk->drv.p->font_width)
267        {
268            uint32_t c = *chars++;
269
270            if(c > 0x00000020 && c < 0x00000080)
271            {
272                glBindTexture(GL_TEXTURE_2D, kk->drv.p->id[c - 32]);
273                glColor4bv(gl_bgpal[attr[0] & 0xf]);
274                glBegin(GL_QUADS);
275                    glTexCoord2f(0, kk->drv.p->sh);
276                    glVertex2f(x, y);
277                    glTexCoord2f(kk->drv.p->sw, kk->drv.p->sh);
278                    glVertex2f(x + kk->drv.p->font_width, y);
279                    glTexCoord2f(kk->drv.p->sw, 0);
280                    glVertex2f(x + kk->drv.p->font_width,
281                               y + kk->drv.p->font_height);
282                    glTexCoord2f(0, 0);
283                    glVertex2f(x, y + kk->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    glutMainLoopEvent();
295    glutSwapBuffers();
296    glutPostRedisplay();
297}
298
299static void gl_handle_resize(caca_t *kk)
300{
301    kk->drv.p->width = kk->drv.p->new_width;
302    kk->drv.p->height = kk->drv.p->new_height;
303
304    glMatrixMode(GL_PROJECTION);
305    glPushMatrix();
306    glLoadIdentity();
307
308    glViewport(0, 0, kk->drv.p->width, kk->drv.p->height);
309    gluOrtho2D(0, kk->drv.p->width, kk->drv.p->height, 0);
310    glMatrixMode(GL_MODELVIEW);
311}
312
313static int gl_get_event(caca_t *kk, struct caca_event *ev)
314{
315    glutMainLoopEvent();
316
317    if(kk->resize.resized)
318    {
319        ev->type = CACA_EVENT_RESIZE;
320        ev->data.resize.w = kk->qq->width;
321        ev->data.resize.h = kk->qq->height;
322        return 1;
323    }
324
325    if(kk->drv.p->mouse_changed)
326    {
327        ev->type = CACA_EVENT_MOUSE_MOTION;
328        ev->data.mouse.x = kk->mouse.x;
329        ev->data.mouse.y = kk->mouse.y;
330        kk->drv.p->mouse_changed = 0;
331
332        if(kk->drv.p->mouse_clicked)
333        {
334            _push_event(kk, ev);
335            ev->type = CACA_EVENT_MOUSE_PRESS;
336            ev->data.mouse.button = kk->drv.p->mouse_button;
337            kk->drv.p->mouse_clicked = 0;
338        }
339
340        return 1;
341    }
342
343    if(kk->drv.p->key != 0)
344    {
345        ev->type = CACA_EVENT_KEY_PRESS;
346        ev->data.key.c = kk->drv.p->key;
347        ev->data.key.ucs4 = (uint32_t)kk->drv.p->key;
348        ev->data.key.utf8[0] = kk->drv.p->key;
349        ev->data.key.utf8[1] = '\0';
350        kk->drv.p->key = 0;
351        return 1;
352    }
353
354    if(kk->drv.p->special_key != 0)
355    {
356        switch(kk->drv.p->special_key)
357        {
358            case GLUT_KEY_F1 : ev->data.key.c = CACA_KEY_F1; break;
359            case GLUT_KEY_F2 : ev->data.key.c = CACA_KEY_F2; break;
360            case GLUT_KEY_F3 : ev->data.key.c = CACA_KEY_F3; break;
361            case GLUT_KEY_F4 : ev->data.key.c = CACA_KEY_F4; break;
362            case GLUT_KEY_F5 : ev->data.key.c = CACA_KEY_F5; break;
363            case GLUT_KEY_F6 : ev->data.key.c = CACA_KEY_F6; break;
364            case GLUT_KEY_F7 : ev->data.key.c = CACA_KEY_F7; break;
365            case GLUT_KEY_F8 : ev->data.key.c = CACA_KEY_F8; break;
366            case GLUT_KEY_F9 : ev->data.key.c = CACA_KEY_F9; break;
367            case GLUT_KEY_F10: ev->data.key.c = CACA_KEY_F10; break;
368            case GLUT_KEY_F11: ev->data.key.c = CACA_KEY_F11; break;
369            case GLUT_KEY_F12: ev->data.key.c = CACA_KEY_F12; break;
370            case GLUT_KEY_LEFT : ev->data.key.c = CACA_KEY_LEFT; break;
371            case GLUT_KEY_RIGHT: ev->data.key.c = CACA_KEY_RIGHT; break;
372            case GLUT_KEY_UP   : ev->data.key.c = CACA_KEY_UP; break;
373            case GLUT_KEY_DOWN : ev->data.key.c = CACA_KEY_DOWN; break;
374            default: ev->type = CACA_EVENT_NONE; return 0;
375        }
376
377        ev->type = CACA_EVENT_KEY_PRESS;
378        ev->data.key.ucs4 = 0;
379        ev->data.key.utf8[0] = '\0';
380
381        kk->drv.p->special_key = 0;
382        return 1;
383    }
384
385    ev->type = CACA_EVENT_NONE;
386    return 0;
387}
388
389/*
390 * XXX: following functions are local
391 */
392
393static void gl_handle_keyboard(unsigned char key, int x, int y)
394{
395    caca_t *kk = gl_kk;
396
397    kk->drv.p->key = key;
398}
399
400static void gl_handle_special_key(int key, int x, int y)
401{
402    caca_t *kk = gl_kk;
403
404    kk->drv.p->special_key = key;
405}
406
407static void gl_handle_reshape(int w, int h)
408{
409    caca_t *kk = gl_kk;
410
411    if(kk->drv.p->bit) /* Do not handle reshaping at the first time */
412    {
413        kk->drv.p->new_width = w;
414        kk->drv.p->new_height = h;
415
416        kk->resize.w = w / kk->drv.p->font_width;
417        kk->resize.h = (h / kk->drv.p->font_height) + 1;
418
419        kk->resize.resized = 1;
420    }
421    else
422        kk->drv.p->bit = 1;
423}
424
425static void gl_handle_mouse(int button, int state, int x, int y)
426{
427    caca_t *kk = gl_kk;
428
429    kk->drv.p->mouse_clicked = 1;
430    kk->drv.p->mouse_button = button;
431    kk->drv.p->mouse_state = state;
432    kk->drv.p->mouse_x = x / kk->drv.p->font_width;
433    kk->drv.p->mouse_y = y / kk->drv.p->font_height;
434    kk->drv.p->mouse_changed = 1;
435}
436
437static void gl_handle_mouse_motion(int x, int y)
438{
439    caca_t *kk = gl_kk;
440
441    kk->drv.p->mouse_x = x / kk->drv.p->font_width;
442    kk->drv.p->mouse_y = y / kk->drv.p->font_height;
443    kk->drv.p->mouse_changed = 1;
444}
445
446/*
447 * Driver initialisation
448 */
449
450int gl_install(caca_t *kk)
451{
452#if defined(HAVE_GETENV) && defined(GLUT_XLIB_IMPLEMENTATION)
453    if(!getenv("DISPLAY") || !*(getenv("DISPLAY")))
454        return -1;
455#endif
456
457    kk->drv.driver = CACA_DRIVER_GL;
458
459    kk->drv.init_graphics = gl_init_graphics;
460    kk->drv.end_graphics = gl_end_graphics;
461    kk->drv.set_window_title = gl_set_window_title;
462    kk->drv.get_window_width = gl_get_window_width;
463    kk->drv.get_window_height = gl_get_window_height;
464    kk->drv.display = gl_display;
465    kk->drv.handle_resize = gl_handle_resize;
466    kk->drv.get_event = gl_get_event;
467
468    return 0;
469}
470
471#endif /* USE_GL */
472
Note: See TracBrowser for help on using the repository browser.