source: libcaca/trunk/src/caca.c @ 521

Last change on this file since 521 was 510, checked in by Sam Hocevar, 17 years ago
  • src/caca.c: on Unix, do not use the GL driver if DISPLAY is not set.
  • Property svn:keywords set to Id
File size: 16.4 KB
Line 
1/*
2 *  libcaca       ASCII-Art library
3 *  Copyright (c) 2002, 2003 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 GNU Lesser General Public
8 *  License as published by the Free Software Foundation; either
9 *  version 2 of the License, or (at your option) any later version.
10 *
11 *  This library is distributed in the hope that it will be useful,
12 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 *  Lesser General Public License for more details.
15 *
16 *  You should have received a copy of the GNU Lesser General Public
17 *  License along with this library; if not, write to the Free Software
18 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
19 *  02111-1307  USA
20 */
21
22/** \file caca.c
23 *  \version \$Id: caca.c 510 2005-11-12 15:30:46Z sam $
24 *  \author Sam Hocevar <sam@zoy.org>
25 *  \brief Main \e libcaca functions
26 *
27 *  This file contains the main functions used by \e libcaca applications to
28 *  initialise the library, get the screen properties, set the framerate and
29 *  so on.
30 */
31
32#include "config.h"
33
34#if defined(USE_SLANG)
35#   if defined(HAVE_SLANG_SLANG_H)
36#       include <slang/slang.h>
37#   else
38#       include <slang.h>
39#   endif
40#endif
41#if defined(USE_NCURSES)
42#   if defined(HAVE_NCURSES_H)
43#       include <ncurses.h>
44#   else
45#       include <curses.h>
46#   endif
47#endif
48#if defined(USE_CONIO)
49#   include <dos.h>
50#   include <conio.h>
51#endif
52#if defined(USE_X11)
53#   include <X11/Xlib.h>
54#endif
55#if defined(USE_WIN32)
56#   include <windows.h>
57#endif
58#if defined(USE_GL)
59#   include <GL/gl.h>
60#endif
61#include <stdlib.h>
62#include <string.h>
63
64#include "caca.h"
65#include "caca_internals.h"
66
67static void caca_init_driver(void);
68static void caca_init_features(void);
69static void caca_init_terminal(void);
70
71#if !defined(_DOXYGEN_SKIP_ME)
72enum caca_driver _caca_driver;
73#endif
74
75#if defined(USE_NCURSES)
76static mmask_t oldmask;
77#endif
78
79#if defined(USE_WIN32)
80static CONSOLE_CURSOR_INFO cci;
81#endif
82
83/** \brief Initialise \e libcaca.
84 *
85 *  This function initialises internal \e libcaca structures and the backend
86 *  that will be used for subsequent graphical operations. It must be the
87 *  first \e libcaca function to be called in a function. caca_end() should
88 *  be called at the end of the program to free all allocated resources.
89 *
90 *  \return 0 upon success, a non-zero value if an error occurs.
91 */
92int caca_init(void)
93{
94#if defined(USE_NCURSES)
95    mmask_t newmask;
96#endif
97
98    caca_init_driver();
99
100    if(_caca_driver == CACA_DRIVER_NONE)
101        return -1;
102
103    caca_init_features();
104    caca_init_terminal();
105
106#if defined(USE_SLANG)
107    if(_caca_driver == CACA_DRIVER_SLANG)
108    {
109        /* Initialise slang library */
110        SLsig_block_signals();
111        SLtt_get_terminfo();
112
113        if(SLkp_init() == -1)
114        {
115            SLsig_unblock_signals();
116            return -1;
117        }
118
119        SLang_init_tty(-1, 0, 1);
120
121        if(SLsmg_init_smg() == -1)
122        {
123            SLsig_unblock_signals();
124            return -1;
125        }
126
127        SLsig_unblock_signals();
128
129        SLsmg_cls();
130        SLtt_set_cursor_visibility(0);
131        SLkp_define_keysym("\e[M", 1001);
132        SLtt_set_mouse_mode(1, 0);
133        SLsmg_refresh();
134
135        /* Disable scrolling so that hashmap scrolling optimization code
136         * does not cause ugly refreshes due to slow terminals */
137        SLtt_Term_Cannot_Scroll = 1;
138    }
139    else
140#endif
141#if defined(USE_NCURSES)
142    if(_caca_driver == CACA_DRIVER_NCURSES)
143    {
144        initscr();
145        keypad(stdscr, TRUE);
146        nonl();
147        raw();
148        noecho();
149        nodelay(stdscr, TRUE);
150        curs_set(0);
151
152        /* Activate mouse */
153        newmask = REPORT_MOUSE_POSITION | ALL_MOUSE_EVENTS;
154        mousemask(newmask, &oldmask);
155        mouseinterval(-1); /* No click emulation */
156
157        /* Set the escape delay to a ridiculously low value */
158        ESCDELAY = 10;
159    }
160    else
161#endif
162#if defined(USE_CONIO)
163    if(_caca_driver == CACA_DRIVER_CONIO)
164    {
165        _wscroll = 0;
166        _setcursortype(_NOCURSOR);
167        clrscr();
168    }
169    else
170#endif
171#if defined(USE_X11)
172    if(_caca_driver == CACA_DRIVER_X11)
173    {
174        /* Nothing to do */
175    }
176    else
177#endif
178#if defined(USE_WIN32)
179    if(_caca_driver == CACA_DRIVER_WIN32)
180    {
181        /* This call is allowed to fail in cas we already have a console */
182        AllocConsole();
183
184        win32_hin = GetStdHandle(STD_INPUT_HANDLE);
185        win32_hout = CreateFile("CONOUT$", GENERIC_READ | GENERIC_WRITE,
186                                FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
187                                OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
188
189        if(win32_hout == INVALID_HANDLE_VALUE)
190            return -1;
191
192        GetConsoleCursorInfo(win32_hout, &cci);
193        cci.bVisible = FALSE;
194        SetConsoleCursorInfo(win32_hout, &cci);
195
196        SetConsoleMode(win32_hout, ENABLE_MOUSE_INPUT);
197    }
198    else
199#endif
200#if defined(USE_GL)
201    if(_caca_driver == CACA_DRIVER_GL)
202    {
203        /* Nothing to do */
204    }
205    else
206#endif
207#if defined(USE_NULL)
208    if(_caca_driver == CACA_DRIVER_NULL)
209    {
210        /* Nothing to do */
211    }
212    else
213#endif
214    {
215        /* Dummy */
216    }
217
218    if(_caca_init_graphics())
219        return -1;
220
221    if(_caca_init_bitmap())
222        return -1;
223
224    return 0;
225}
226
227/** \brief Get the screen width.
228 *
229 *  This function returns the current screen width, in character cells.
230 *
231 *  \return The screen width.
232 */
233unsigned int caca_get_width(void)
234{
235    return _caca_width;
236}
237
238/** \brief Get the screen height.
239 *
240 *  This function returns the current screen height, in character cells.
241 *
242 *  \return The screen height.
243 */
244unsigned int caca_get_height(void)
245{
246    return _caca_height;
247}
248
249/** \brief Translate a colour index into the colour's name.
250 *
251 *  This function translates a caca_color enum into a human-readable
252 *  description string of the associated colour.
253 *
254 *  \param color The colour value.
255 *  \return A static string containing the colour's name.
256 */
257char const *caca_get_color_name(enum caca_color color)
258{
259    static char const *color_names[] =
260    {
261        "black",
262        "blue",
263        "green",
264        "cyan",
265        "red",
266        "magenta",
267        "brown",
268        "light gray",
269        "dark gray",
270        "light blue",
271        "light green",
272        "light cyan",
273        "light red",
274        "light magenta",
275        "yellow",
276        "white",
277    };
278
279    if(color < 0 || color > 15)
280        return "unknown";
281
282    return color_names[color];
283}
284
285/** \brief Get the current value of a feature.
286 *
287 *  This function retrieves the value of an internal \e libcaca feature. A
288 *  generic feature value is expected, such as CACA_ANTIALIASING.
289 *
290 *  \param feature The requested feature.
291 *  \return The current value of the feature or CACA_FEATURE_UNKNOWN if an
292 *          error occurred..
293 */
294enum caca_feature caca_get_feature(enum caca_feature feature)
295{
296    switch(feature)
297    {
298        case CACA_BACKGROUND:
299            return _caca_background;
300        case CACA_ANTIALIASING:
301            return _caca_antialiasing;
302        case CACA_DITHERING:
303            return _caca_dithering;
304
305        default:
306            return CACA_FEATURE_UNKNOWN;
307    }
308}
309
310/** \brief Set a feature.
311 *
312 *  This function sets an internal \e libcaca feature such as the antialiasing
313 *  or dithering modes. If a specific feature such as CACA_DITHERING_RANDOM,
314 *  caca_set_feature() will set it immediately. If a generic feature is given
315 *  instead, such as CACA_DITHERING, the default value will be used instead.
316 *
317 *  \param feature The requested feature.
318 */
319void caca_set_feature(enum caca_feature feature)
320{
321    switch(feature)
322    {
323        case CACA_BACKGROUND:
324            feature = CACA_BACKGROUND_SOLID;
325        case CACA_BACKGROUND_BLACK:
326        case CACA_BACKGROUND_SOLID:
327            _caca_background = feature;
328            break;
329
330        case CACA_ANTIALIASING:
331            feature = CACA_ANTIALIASING_PREFILTER;
332        case CACA_ANTIALIASING_NONE:
333        case CACA_ANTIALIASING_PREFILTER:
334            _caca_antialiasing = feature;
335            break;
336
337        case CACA_DITHERING:
338            feature = CACA_DITHERING_FSTEIN;
339        case CACA_DITHERING_NONE:
340        case CACA_DITHERING_ORDERED2:
341        case CACA_DITHERING_ORDERED4:
342        case CACA_DITHERING_ORDERED8:
343        case CACA_DITHERING_RANDOM:
344        case CACA_DITHERING_FSTEIN:
345            _caca_dithering = feature;
346            break;
347
348        case CACA_FEATURE_UNKNOWN:
349            break;
350    }
351}
352
353/** \brief Translate a feature value into the feature's name.
354 *
355 *  This function translates a caca_feature enum into a human-readable
356 *  description string of the associated feature.
357 *
358 *  \param feature The feature value.
359 *  \return A static string containing the feature's name.
360 */
361char const *caca_get_feature_name(enum caca_feature feature)
362{
363    switch(feature)
364    {
365        case CACA_BACKGROUND_BLACK: return "black background";
366        case CACA_BACKGROUND_SOLID: return "solid background";
367
368        case CACA_ANTIALIASING_NONE:      return "no antialiasing";
369        case CACA_ANTIALIASING_PREFILTER: return "prefilter antialiasing";
370
371        case CACA_DITHERING_NONE:     return "no dithering";
372        case CACA_DITHERING_ORDERED2: return "2x2 ordered dithering";
373        case CACA_DITHERING_ORDERED4: return "4x4 ordered dithering";
374        case CACA_DITHERING_ORDERED8: return "8x8 ordered dithering";
375        case CACA_DITHERING_RANDOM:   return "random dithering";
376        case CACA_DITHERING_FSTEIN:   return "Floyd-Steinberg dithering";
377
378        default: return "unknown";
379    }
380}
381
382/** \brief Uninitialise \e libcaca.
383 *
384 *  This function frees all resources allocated by caca_init(). After
385 *  caca_end() has been called, no other \e libcaca functions may be used
386 *  unless a new call to caca_init() is done.
387 */
388void caca_end(void)
389{
390    _caca_end_bitmap();
391    _caca_end_graphics();
392
393#if defined(USE_SLANG)
394    if(_caca_driver == CACA_DRIVER_SLANG)
395    {
396        SLtt_set_mouse_mode(0, 0);
397        SLtt_set_cursor_visibility(1);
398        SLang_reset_tty();
399        SLsmg_reset_smg();
400    }
401    else
402#endif
403#if defined(USE_NCURSES)
404    if(_caca_driver == CACA_DRIVER_NCURSES)
405    {
406        mousemask(oldmask, NULL);
407        curs_set(1);
408        noraw();
409        endwin();
410    }
411    else
412#endif
413#if defined(USE_CONIO)
414    if(_caca_driver == CACA_DRIVER_CONIO)
415    {
416        _wscroll = 1;
417        textcolor((enum COLORS)WHITE);
418        textbackground((enum COLORS)BLACK);
419        gotoxy(_caca_width, _caca_height);
420        cputs("\r\n");
421        _setcursortype(_NORMALCURSOR);
422    }
423    else
424#endif
425#if defined(USE_X11)
426    if(_caca_driver == CACA_DRIVER_X11)
427    {
428        /* Nothing to do */
429    }
430    else
431#endif
432#if defined(USE_WIN32)
433    if(_caca_driver == CACA_DRIVER_WIN32)
434    {
435        SetConsoleTextAttribute(win32_hout, FOREGROUND_INTENSITY
436                                             | FOREGROUND_RED
437                                             | FOREGROUND_GREEN
438                                             | FOREGROUND_BLUE);
439        cci.bVisible = TRUE;
440        SetConsoleCursorInfo(win32_hout, &cci);
441        CloseHandle(win32_hout);
442    }
443    else
444#endif
445#if defined(USE_GL)
446    if(_caca_driver == CACA_DRIVER_GL)
447    {
448        /* Nothing to do */
449    }
450    else
451#endif
452#if defined(USE_NULL)
453    if(_caca_driver == CACA_DRIVER_NULL)
454    {
455        /* Nothing to do */
456    }
457    else
458#endif
459    {
460        /* Dummy */
461    }
462}
463
464/*
465 * XXX: The following functions are local.
466 */
467
468static void caca_init_driver(void)
469{
470#if defined(HAVE_GETENV) && defined(HAVE_STRCASECMP)
471    char *var = getenv("CACA_DRIVER");
472
473    /* If the environment variable was set, use it */
474    if(var && *var)
475    {
476#if defined(USE_WIN32)
477        if(!strcasecmp(var, "win32"))
478            _caca_driver = CACA_DRIVER_WIN32;
479        else
480#endif
481#if defined(USE_CONIO)
482        if(!strcasecmp(var, "conio"))
483            _caca_driver = CACA_DRIVER_CONIO;
484        else
485#endif
486#if defined(USE_X11)
487        if(!strcasecmp(var, "x11"))
488            _caca_driver = CACA_DRIVER_X11;
489        else
490#endif
491#if defined(USE_GL)
492        if(!strcasecmp(var, "gl"))
493            _caca_driver = CACA_DRIVER_GL;
494        else
495#endif
496#if defined(USE_SLANG)
497        if(!strcasecmp(var, "slang"))
498            _caca_driver = CACA_DRIVER_SLANG;
499        else
500#endif
501#if defined(USE_NCURSES)
502        if(!strcasecmp(var, "ncurses"))
503            _caca_driver = CACA_DRIVER_NCURSES;
504        else
505#endif
506#if defined(USE_NULL)
507        if(!strcasecmp(var, "null"))
508            _caca_driver = CACA_DRIVER_NULL;
509        else
510#endif
511
512            _caca_driver = CACA_DRIVER_NONE;
513
514        return;
515    }
516#endif
517
518#if defined(USE_WIN32)
519    _caca_driver = CACA_DRIVER_WIN32;
520    return;
521#endif
522#if defined(USE_CONIO)
523    _caca_driver = CACA_DRIVER_CONIO;
524    return;
525#endif
526#if defined(USE_X11)
527#if defined(HAVE_GETENV)
528    if(getenv("DISPLAY") && *(getenv("DISPLAY")))
529#endif
530    {
531        _caca_driver = CACA_DRIVER_X11;
532        return;
533    }
534#endif
535#if defined(USE_GL)
536#if defined(HAVE_GETENV) && defined(GLUT_XLIB_IMPLEMENTATION)
537    if(getenv("DISPLAY") && *(getenv("DISPLAY")))
538#endif
539    {
540        _caca_driver = CACA_DRIVER_GL;
541        return;
542    }
543#endif
544#if defined(USE_SLANG)
545    _caca_driver = CACA_DRIVER_SLANG;
546    return;
547#endif
548#if defined(USE_NCURSES)
549    _caca_driver = CACA_DRIVER_NCURSES;
550    return;
551#endif
552#if defined(USE_NULL)
553    _caca_driver = CACA_DRIVER_NULL;
554    return;
555#endif
556
557    _caca_driver = CACA_DRIVER_NONE;
558    return;
559}
560
561static void caca_init_features(void)
562{
563    /* FIXME: if strcasecmp isn't available, use strcmp */
564#if defined(HAVE_GETENV) && defined(HAVE_STRCASECMP)
565    char *var;
566#endif
567
568    caca_set_feature(CACA_BACKGROUND);
569    caca_set_feature(CACA_ANTIALIASING);
570    caca_set_feature(CACA_DITHERING);
571
572#if defined(HAVE_GETENV) && defined(HAVE_STRCASECMP)
573    if((var = getenv("CACA_BACKGROUND")) && *var)
574    {
575        if(!strcasecmp("black", var))
576            caca_set_feature(CACA_BACKGROUND_BLACK);
577        else if(!strcasecmp("solid", var))
578            caca_set_feature(CACA_BACKGROUND_SOLID);
579    }
580
581    if((var = getenv("CACA_ANTIALIASING")) && *var)
582    {
583        if(!strcasecmp("none", var))
584            caca_set_feature(CACA_ANTIALIASING_NONE);
585        else if(!strcasecmp("prefilter", var))
586            caca_set_feature(CACA_ANTIALIASING_PREFILTER);
587    }
588
589    if((var = getenv("CACA_DITHERING")) && *var)
590    {
591        if(!strcasecmp("none", var))
592            caca_set_feature(CACA_DITHERING_NONE);
593        else if(!strcasecmp("ordered2", var))
594            caca_set_feature(CACA_DITHERING_ORDERED2);
595        else if(!strcasecmp("ordered4", var))
596            caca_set_feature(CACA_DITHERING_ORDERED4);
597        else if(!strcasecmp("ordered8", var))
598            caca_set_feature(CACA_DITHERING_ORDERED8);
599        else if(!strcasecmp("random", var))
600            caca_set_feature(CACA_DITHERING_RANDOM);
601        else if(!strcasecmp("fstein", var))
602            caca_set_feature(CACA_DITHERING_FSTEIN);
603    }
604#endif
605}
606
607static void caca_init_terminal(void)
608{
609#if defined(HAVE_GETENV) && defined(HAVE_PUTENV) && \
610     (defined(USE_SLANG) || defined(USE_NCURSES))
611    char *term, *colorterm, *other;
612#endif
613
614#if defined(USE_SLANG)
615    if(_caca_driver != CACA_DRIVER_SLANG)
616#endif
617#if defined(USE_NCURSES)
618    if(_caca_driver != CACA_DRIVER_NCURSES)
619#endif
620    return;
621
622#if defined(HAVE_GETENV) && defined(HAVE_PUTENV) && \
623     (defined(USE_SLANG) || defined(USE_NCURSES))
624    term = getenv("TERM");
625    colorterm = getenv("COLORTERM");
626
627    if(term && !strcmp(term, "xterm"))
628    {
629        /* If we are using gnome-terminal, it's really a 16 colour terminal */
630        if(colorterm && !strcmp(colorterm, "gnome-terminal"))
631        {
632#if defined(USE_NCURSES)
633            if(_caca_driver == CACA_DRIVER_NCURSES)
634            {
635                SCREEN *screen;
636                screen = newterm("xterm-16color", stdout, stdin);
637                if(screen == NULL)
638                    return;
639                endwin();
640            }
641#endif
642            (void)putenv("TERM=xterm-16color");
643            return;
644        }
645
646        /* Ditto if we are using Konsole */
647        other = getenv("KONSOLE_DCOP_SESSION");
648        if(other)
649        {
650#if defined(USE_NCURSES)
651            if(_caca_driver == CACA_DRIVER_NCURSES)
652            {
653                SCREEN *screen;
654                screen = newterm("xterm-16color", stdout, stdin);
655                if(screen == NULL)
656                    return;
657                endwin();
658            }
659#endif
660            (void)putenv("TERM=xterm-16color");
661            return;
662        }
663    }
664#endif
665}
666
Note: See TracBrowser for help on using the repository browser.