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

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