source: libcaca/trunk/caca/driver/gl.c @ 4333

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

Large source code cleanup, getting rid of spaces, tabs, and svn keywords.

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