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

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