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

Last change on this file since 1430 was 1430, checked in by Sam Hocevar, 14 years ago
  • Added caca_set_cursor(). Not yet functional.
  • Property svn:keywords set to Id
File size: 16.3 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 1430 2006-11-24 05:51:10Z sam $
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/*
43 * Global variables
44 */
45
46static caca_display_t *gl_d; /* FIXME: we ought to get rid of this */
47
48/*
49 * Local functions
50 */
51static void gl_handle_keyboard(unsigned char, int, int);
52static void gl_handle_special_key(int, int, int);
53static void gl_handle_reshape(int, int);
54static void gl_handle_mouse(int, int, int, int);
55static void gl_handle_mouse_motion(int, int);
56#ifdef HAVE_GLUTCLOSEFUNC
57static void gl_handle_close(void);
58#endif
59static void _display(void);
60static void gl_compute_font(caca_display_t *);
61
62struct driver_private
63{
64    int window;
65    unsigned int width, height;
66    unsigned int new_width, new_height;
67    cucul_font_t *f;
68    float font_width, font_height;
69    float incx, incy;
70    unsigned long int const *blocks;
71    int *txid;
72    unsigned char close;
73    unsigned char bit;
74    unsigned char mouse_changed, mouse_clicked;
75    unsigned int mouse_x, mouse_y;
76    unsigned int mouse_button, mouse_state;
77
78    unsigned char key;
79    int special_key;
80
81    float sw, sh;
82};
83
84static int gl_init_graphics(caca_display_t *dp)
85{
86    char const *geometry;
87    char *argv[2] = { "", NULL };
88    char const * const * fonts;
89    unsigned int width = dp->cv->width, height = dp->cv->height;
90    int argc = 1;
91
92    dp->drv.p = malloc(sizeof(struct driver_private));
93
94    gl_d = dp;
95
96#if defined(HAVE_GETENV)
97    geometry = getenv("CACA_GEOMETRY");
98    if(geometry && *geometry)
99        sscanf(geometry, "%ux%u", &width, &height);
100#endif
101
102    _cucul_set_canvas_size(dp->cv, width ? width : 80, height ? height : 32);
103
104    /* Load a libcucul internal font */
105    fonts = cucul_get_font_list();
106    if(fonts[0] == NULL)
107    {
108        fprintf(stderr, "error: libcucul was compiled without any fonts\n");
109        return -1;
110    }
111    dp->drv.p->f = cucul_load_font(fonts[0], 0);
112    if(dp->drv.p->f == NULL)
113    {
114        fprintf(stderr, "error: could not load font \"%s\"\n", fonts[0]);
115        return -1;
116    }
117
118    dp->drv.p->font_width = cucul_get_font_width(dp->drv.p->f);
119    dp->drv.p->font_height = cucul_get_font_height(dp->drv.p->f);
120
121    dp->drv.p->width = dp->cv->width * dp->drv.p->font_width;
122    dp->drv.p->height = dp->cv->height * dp->drv.p->font_height;
123
124#ifdef HAVE_GLUTCLOSEFUNC
125    dp->drv.p->close = 0;
126#endif
127    dp->drv.p->bit = 0;
128
129    dp->drv.p->mouse_changed = dp->drv.p->mouse_clicked = 0;
130    dp->drv.p->mouse_button = dp->drv.p->mouse_state = 0;
131
132    dp->drv.p->key = 0;
133    dp->drv.p->special_key = 0;
134
135    dp->drv.p->sw = ((float)dp->drv.p->font_width) / 16.0f;
136    dp->drv.p->sh = ((float)dp->drv.p->font_height) / 16.0f;
137
138    glutInit(&argc, argv);
139
140    glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);
141    glutInitWindowSize(dp->drv.p->width, dp->drv.p->height);
142    dp->drv.p->window = glutCreateWindow("caca for GL");
143
144    gluOrtho2D(0, dp->drv.p->width, dp->drv.p->height, 0);
145
146    glDisable(GL_CULL_FACE);
147    glDisable(GL_DEPTH_TEST);
148
149    glutKeyboardFunc(gl_handle_keyboard);
150    glutSpecialFunc(gl_handle_special_key);
151    glutReshapeFunc(gl_handle_reshape);
152    glutDisplayFunc(_display);
153
154#ifdef HAVE_GLUTCLOSEFUNC
155    glutCloseFunc(gl_handle_close);
156#endif
157
158    glutMouseFunc(gl_handle_mouse);
159    glutMotionFunc(gl_handle_mouse_motion);
160    glutPassiveMotionFunc(gl_handle_mouse_motion);
161
162    glLoadIdentity();
163
164    glMatrixMode(GL_PROJECTION);
165    glPushMatrix();
166    glLoadIdentity();
167    gluOrtho2D(0, dp->drv.p->width, dp->drv.p->height, 0);
168
169    glMatrixMode(GL_MODELVIEW);
170
171    glClear(GL_COLOR_BUFFER_BIT);
172    glEnable(GL_TEXTURE_2D);
173    glEnable(GL_BLEND);
174    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
175
176    glEnable(GL_TEXTURE_2D);
177
178    gl_compute_font(dp);
179
180    return 0;
181}
182
183static int gl_end_graphics(caca_display_t *dp)
184{
185    glutDestroyWindow(dp->drv.p->window);
186    free(dp->drv.p->txid);
187    free(dp->drv.p);
188    return 0;
189}
190
191static int gl_set_display_title(caca_display_t *dp, char const *title)
192{
193    glutSetWindowTitle(title);
194    return 0;
195}
196
197static unsigned int gl_get_display_width(caca_display_t *dp)
198{
199    return dp->drv.p->width;
200}
201
202static unsigned int gl_get_display_height(caca_display_t *dp)
203{
204    return dp->drv.p->height;
205}
206
207static void gl_display(caca_display_t *dp)
208{
209    unsigned int x, y, line;
210
211    glClear(GL_COLOR_BUFFER_BIT);
212    glDisable(GL_TEXTURE_2D);
213    glDisable(GL_BLEND);
214    line = 0;
215    for(y = 0; y < dp->drv.p->height; y += dp->drv.p->font_height)
216    {
217        uint32_t *attrs = dp->cv->attrs + line * dp->cv->width;
218
219        /* FIXME: optimise using stride */
220        for(x = 0; x < dp->drv.p->width; x += dp->drv.p->font_width)
221        {
222            uint16_t bg = _cucul_attr_to_rgb12bg(*attrs++);
223
224            glColor4b(((bg & 0xf00) >> 8) * 8,
225                      ((bg & 0x0f0) >> 4) * 8,
226                      (bg & 0x00f) * 8,
227                      0xff);
228            glBegin(GL_QUADS);
229            glVertex2f(x, y);
230            glVertex2f(x + dp->drv.p->font_width, y);
231            glVertex2f(x + dp->drv.p->font_width,
232                       y + dp->drv.p->font_height);
233            glVertex2f(x, y + dp->drv.p->font_height);
234            glEnd();
235        }
236
237        line++;
238    }
239
240    /* 2nd pass, avoids changing render state too much */
241    glEnable(GL_TEXTURE_2D);
242    glEnable(GL_BLEND);
243
244    line = 0;
245    for(y = 0; y < dp->drv.p->height; y += dp->drv.p->font_height, line++)
246    {
247        uint32_t *attrs = dp->cv->attrs + line * dp->cv->width;
248        uint32_t *chars = dp->cv->chars + line * dp->cv->width;
249
250        for(x = 0; x < dp->drv.p->width; x += dp->drv.p->font_width, attrs++)
251        {
252            uint32_t ch = *chars++;
253            uint16_t fg;
254            int i, b, fullwidth;
255
256            fullwidth = cucul_utf32_is_fullwidth(ch);
257
258            for(b = 0, i = 0; dp->drv.p->blocks[i + 1]; i += 2)
259            {
260                if(ch < (uint32_t)dp->drv.p->blocks[i])
261                     break;
262
263                if(ch >= (uint32_t)dp->drv.p->blocks[i + 1])
264                {
265                    b += (uint32_t)(dp->drv.p->blocks[i + 1]
266                                     - dp->drv.p->blocks[i]);
267                    continue;
268                }
269
270                glBindTexture(GL_TEXTURE_2D,
271                              dp->drv.p->txid[b + ch
272                                        - (uint32_t)dp->drv.p->blocks[i]]);
273
274                fg = _cucul_attr_to_rgb12fg(*attrs);
275                glColor3b(((fg & 0xf00) >> 8) * 8,
276                          ((fg & 0x0f0) >> 4) * 8,
277                          (fg & 0x00f) * 8);
278                /* FIXME: handle fullwidth glyphs here */
279                glBegin(GL_QUADS);
280                glTexCoord2f(0, dp->drv.p->sh);
281                glVertex2f(x, y);
282                glTexCoord2f(dp->drv.p->sw, dp->drv.p->sh);
283                glVertex2f(x + dp->drv.p->font_width, y);
284                glTexCoord2f(dp->drv.p->sw, 0);
285                glVertex2f(x + dp->drv.p->font_width,
286                           y + dp->drv.p->font_height);
287                glTexCoord2f(0, 0);
288                glVertex2f(x, y + dp->drv.p->font_height);
289                glEnd();
290            }
291
292            if(fullwidth)
293            {
294                chars++; attrs++; x += dp->drv.p->font_width;
295            }
296        }
297    }
298
299#ifdef HAVE_GLUTCHECKLOOP
300    glutCheckLoop();
301#else
302    glutMainLoopEvent();
303#endif
304    glutSwapBuffers();
305    glutPostRedisplay();
306}
307
308static void gl_handle_resize(caca_display_t *dp)
309{
310    dp->drv.p->width = dp->drv.p->new_width;
311    dp->drv.p->height = dp->drv.p->new_height;
312
313    glMatrixMode(GL_PROJECTION);
314    glPushMatrix();
315    glLoadIdentity();
316
317    glViewport(0, 0, dp->drv.p->width, dp->drv.p->height);
318    gluOrtho2D(0, dp->drv.p->width, dp->drv.p->height, 0);
319    glMatrixMode(GL_MODELVIEW);
320}
321
322static int gl_get_event(caca_display_t *dp, caca_event_t *ev)
323{
324#ifdef HAVE_GLUTCHECKLOOP
325    glutCheckLoop();
326#else
327    glutMainLoopEvent();
328#endif
329
330#ifdef HAVE_GLUTCLOSEFUNC
331    if(dp->drv.p->close)
332    {
333        dp->drv.p->close = 0;
334        ev->type = CACA_EVENT_QUIT;
335        return 1;
336    }
337#endif
338
339    if(dp->resize.resized)
340    {
341        ev->type = CACA_EVENT_RESIZE;
342        ev->data.resize.w = dp->cv->width;
343        ev->data.resize.h = dp->cv->height;
344        return 1;
345    }
346
347    if(dp->drv.p->mouse_changed)
348    {
349        ev->type = CACA_EVENT_MOUSE_MOTION;
350        ev->data.mouse.x = dp->mouse.x;
351        ev->data.mouse.y = dp->mouse.y;
352        dp->drv.p->mouse_changed = 0;
353
354        if(dp->drv.p->mouse_clicked)
355        {
356            _push_event(dp, ev);
357            ev->type = CACA_EVENT_MOUSE_PRESS;
358            ev->data.mouse.button = dp->drv.p->mouse_button;
359            dp->drv.p->mouse_clicked = 0;
360        }
361
362        return 1;
363    }
364
365    if(dp->drv.p->key != 0)
366    {
367        ev->type = CACA_EVENT_KEY_PRESS;
368        ev->data.key.ch = dp->drv.p->key;
369        ev->data.key.utf32 = (uint32_t)dp->drv.p->key;
370        ev->data.key.utf8[0] = dp->drv.p->key;
371        ev->data.key.utf8[1] = '\0';
372        dp->drv.p->key = 0;
373        return 1;
374    }
375
376    if(dp->drv.p->special_key != 0)
377    {
378        switch(dp->drv.p->special_key)
379        {
380            case GLUT_KEY_F1 : ev->data.key.ch = CACA_KEY_F1; break;
381            case GLUT_KEY_F2 : ev->data.key.ch = CACA_KEY_F2; break;
382            case GLUT_KEY_F3 : ev->data.key.ch = CACA_KEY_F3; break;
383            case GLUT_KEY_F4 : ev->data.key.ch = CACA_KEY_F4; break;
384            case GLUT_KEY_F5 : ev->data.key.ch = CACA_KEY_F5; break;
385            case GLUT_KEY_F6 : ev->data.key.ch = CACA_KEY_F6; break;
386            case GLUT_KEY_F7 : ev->data.key.ch = CACA_KEY_F7; break;
387            case GLUT_KEY_F8 : ev->data.key.ch = CACA_KEY_F8; break;
388            case GLUT_KEY_F9 : ev->data.key.ch = CACA_KEY_F9; break;
389            case GLUT_KEY_F10: ev->data.key.ch = CACA_KEY_F10; break;
390            case GLUT_KEY_F11: ev->data.key.ch = CACA_KEY_F11; break;
391            case GLUT_KEY_F12: ev->data.key.ch = CACA_KEY_F12; break;
392            case GLUT_KEY_LEFT : ev->data.key.ch = CACA_KEY_LEFT; break;
393            case GLUT_KEY_RIGHT: ev->data.key.ch = CACA_KEY_RIGHT; break;
394            case GLUT_KEY_UP   : ev->data.key.ch = CACA_KEY_UP; break;
395            case GLUT_KEY_DOWN : ev->data.key.ch = CACA_KEY_DOWN; break;
396            case GLUT_KEY_PAGE_UP : ev->data.key.ch = CACA_KEY_PAGEUP; break;
397            case GLUT_KEY_PAGE_DOWN  : ev->data.key.ch = CACA_KEY_PAGEDOWN;
398                break;
399            case GLUT_KEY_HOME : ev->data.key.ch = CACA_KEY_HOME; break;
400            case GLUT_KEY_END : ev->data.key.ch = CACA_KEY_END; break;
401            case GLUT_KEY_INSERT : ev->data.key.ch = CACA_KEY_INSERT; break;
402
403            default: ev->type = CACA_EVENT_NONE; return 0;
404        }
405
406        ev->type = CACA_EVENT_KEY_PRESS;
407        ev->data.key.utf32 = 0;
408        ev->data.key.utf8[0] = '\0';
409
410        dp->drv.p->special_key = 0;
411        return 1;
412    }
413
414    ev->type = CACA_EVENT_NONE;
415    return 0;
416}
417
418
419static void gl_set_mouse(caca_display_t *dp, int flag)
420{
421    if(flag)
422        glutSetCursor(GLUT_CURSOR_RIGHT_ARROW);
423    else
424        glutSetCursor(GLUT_CURSOR_NONE);
425}
426
427/*
428 * XXX: following functions are local
429 */
430
431static void gl_handle_keyboard(unsigned char key, int x, int y)
432{
433    caca_display_t *dp = gl_d;
434
435    dp->drv.p->key = key;
436}
437
438static void gl_handle_special_key(int key, int x, int y)
439{
440    caca_display_t *dp = gl_d;
441
442    dp->drv.p->special_key = key;
443}
444
445static void gl_handle_reshape(int w, int h)
446{
447    caca_display_t *dp = gl_d;
448
449    if(dp->drv.p->bit) /* Do not handle reshaping at the first time */
450    {
451        dp->drv.p->new_width = w;
452        dp->drv.p->new_height = h;
453
454        dp->resize.w = w / dp->drv.p->font_width;
455        dp->resize.h = (h / dp->drv.p->font_height) + 1;
456
457        dp->resize.resized = 1;
458    }
459    else
460        dp->drv.p->bit = 1;
461}
462
463static void gl_handle_mouse(int button, int state, int x, int y)
464{
465    caca_display_t *dp = gl_d;
466
467    dp->drv.p->mouse_clicked = 1;
468    dp->drv.p->mouse_button = button;
469    dp->drv.p->mouse_state = state;
470    dp->drv.p->mouse_x = x / dp->drv.p->font_width;
471    dp->drv.p->mouse_y = y / dp->drv.p->font_height;
472    dp->mouse.x = dp->drv.p->mouse_x;
473    dp->mouse.y = dp->drv.p->mouse_y;
474    dp->drv.p->mouse_changed = 1;
475}
476
477static void gl_handle_mouse_motion(int x, int y)
478{
479    caca_display_t *dp = gl_d;
480    dp->drv.p->mouse_x = x / dp->drv.p->font_width;
481    dp->drv.p->mouse_y = y / dp->drv.p->font_height;
482    dp->mouse.x = dp->drv.p->mouse_x;
483    dp->mouse.y = dp->drv.p->mouse_y;
484    dp->drv.p->mouse_changed = 1;
485}
486
487#ifdef HAVE_GLUTCLOSEFUNC
488static void gl_handle_close(void)
489{
490    caca_display_t *dp = gl_d;
491    dp->drv.p->close = 1;
492}
493#endif
494
495static void _display(void)
496{
497    caca_display_t *dp = gl_d;
498    gl_display(dp);
499}
500
501static void gl_compute_font(caca_display_t *dp)
502{
503    cucul_canvas_t *cv;
504    uint32_t *image;
505    int i, b, w, h, x, y;
506
507    /* Count how many glyphs this font has */
508    dp->drv.p->blocks = cucul_get_font_blocks(dp->drv.p->f);
509
510    for(b = 0, i = 0; dp->drv.p->blocks[i + 1]; i += 2)
511        b += (int)(dp->drv.p->blocks[i + 1] - dp->drv.p->blocks[i]);
512
513    /* Allocate a libcucul canvas and print all the glyphs on it */
514    cv = cucul_create_canvas(1, b);
515    cucul_set_color_ansi(cv, CUCUL_WHITE, CUCUL_BLACK);
516
517    for(b = 0, i = 0; dp->drv.p->blocks[i + 1]; i += 2)
518    {
519        int j, n = (int)(dp->drv.p->blocks[i + 1] - dp->drv.p->blocks[i]);
520
521        for(j = 0; j < n; j++)
522            cucul_put_char(cv, 0, b + j, dp->drv.p->blocks[i] + j);
523
524        b += n;
525    }
526
527    /* Draw the cucul canvas onto an image buffer */
528    image = malloc(b * dp->drv.p->font_height
529                     * dp->drv.p->font_width * sizeof(uint32_t));
530    cucul_render_canvas(cv, dp->drv.p->f, image, dp->drv.p->font_width,
531                        b * dp->drv.p->font_height, 4 * dp->drv.p->font_width);
532    cucul_free_canvas(cv);
533
534    /* Convert all glyphs in the image buffer to GL textures */
535    dp->drv.p->txid = malloc(b * sizeof(int));
536
537    w = dp->drv.p->font_width <= 16 ? dp->drv.p->font_width : 16;
538    h = dp->drv.p->font_height <= 16 ? dp->drv.p->font_height : 16;
539
540    for(i = 0; i < b; i++)
541    {
542        uint8_t tmp[16 * 4 * 16];
543        uint32_t *glyph = image + (int)(i * dp->drv.p->font_width
544                                          * dp->drv.p->font_height);
545        memset(tmp, 0, 16 * 4 * 16);
546
547        for(y = 0; y < h; y++)
548        {
549            for(x = 0; x < w; x++)
550            {
551                uint32_t offset = x + (15 - y) * 16;
552                uint8_t c = glyph[x + y * (int)dp->drv.p->font_width] >> 8;
553                tmp[offset * 4] = c;
554                tmp[offset * 4 + 1] = c;
555                tmp[offset * 4 + 2] = c;
556                tmp[offset * 4 + 3] = c;
557            }
558        }
559
560        glGenTextures(1, (GLuint*)&dp->drv.p->txid[i]);
561        glBindTexture(GL_TEXTURE_2D, dp->drv.p->txid[i]);
562        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
563        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
564        glTexImage2D(GL_TEXTURE_2D, 0, 4, 16, 16, 0,
565                     GL_RGBA, GL_UNSIGNED_BYTE, tmp);
566    }
567
568    free(image);
569}
570
571/*
572 * Driver initialisation
573 */
574
575int gl_install(caca_display_t *dp)
576{
577#if defined(HAVE_GETENV) && defined(GLUT_XLIB_IMPLEMENTATION)
578    if(!getenv("DISPLAY") || !*(getenv("DISPLAY")))
579        return -1;
580#endif
581
582    dp->drv.driver = CACA_DRIVER_GL;
583
584    dp->drv.init_graphics = gl_init_graphics;
585    dp->drv.end_graphics = gl_end_graphics;
586    dp->drv.set_display_title = gl_set_display_title;
587    dp->drv.get_display_width = gl_get_display_width;
588    dp->drv.get_display_height = gl_get_display_height;
589    dp->drv.display = gl_display;
590    dp->drv.handle_resize = gl_handle_resize;
591    dp->drv.get_event = gl_get_event;
592    dp->drv.set_mouse = gl_set_mouse;
593    dp->drv.set_cursor = NULL;
594
595    return 0;
596}
597
598#endif /* USE_GL */
599
Note: See TracBrowser for help on using the repository browser.