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

Last change on this file since 2821 was 2821, checked in by Sam Hocevar, 11 years ago

Starting refactoring to get rid of libcucul. The initial reason for the
split is rendered moot by the plugin system: when enabled, binaries do
not link directly with libX11 or libGL. I hope this is a step towards
more consisteny and clarity.

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