source: libcaca/branches/0.5/src/graphics.c @ 4104

Last change on this file since 4104 was 281, checked in by Sam Hocevar, 16 years ago
  • NOTES BUGS: + Updated information about the S-Lang colour pair shortage bug.
  • src/graphics.c: + Implemented the solution explained in NOTES.
  • Property svn:keywords set to Id
File size: 29.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 graphics.c
23 *  \version \$Id: graphics.c 281 2003-12-25 21:28:55Z sam $
24 *  \author Sam Hocevar <sam@zoy.org>
25 *  \brief Character drawing functions
26 *
27 *  This file contains character and string drawing functions.
28 */
29
30#include "config.h"
31
32#if defined(USE_SLANG)
33#   include <slang.h>
34#endif
35#if defined(USE_NCURSES)
36#   include <curses.h>
37#endif
38#if defined(USE_CONIO)
39#   include <conio.h>
40#   if defined(SCREENUPDATE_IN_PC_H)
41#       include <pc.h>
42#   endif
43#endif
44#if defined(USE_X11)
45#   include <X11/Xlib.h>
46#endif
47
48#if defined(HAVE_INTTYPES_H)
49#   include <inttypes.h>
50#endif
51
52#include <stdio.h> /* BUFSIZ */
53#include <string.h>
54#include <stdlib.h>
55#include <unistd.h>
56#include <stdarg.h>
57#include <sys/time.h>
58#include <time.h>
59
60#include "caca.h"
61#include "caca_internals.h"
62
63/*
64 * Global variables
65 */
66unsigned int _caca_width = 0;
67unsigned int _caca_height = 0;
68
69/*
70 * Local variables
71 */
72#if defined(USE_NCURSES)
73static int ncurses_attr[16*16];
74#endif
75
76#if defined(USE_SLANG)
77static int slang_assoc[16*16];
78#endif
79
80#if defined(USE_CONIO)
81static struct text_info conio_ti;
82static char *conio_screen;
83#endif
84
85#if defined(USE_X11)
86Display *x11_dpy;
87Window x11_window;
88int x11_font_width, x11_font_height;
89static GC x11_gc;
90static Pixmap x11_pixmap;
91static int *x11_screen;
92static int x11_colors[16];
93static Font x11_font;
94static XFontStruct *x11_font_struct;
95static int x11_font_offset;
96#endif
97
98static char *_caca_empty_line;
99static char *_caca_scratch_line;
100
101static unsigned int _caca_delay;
102static unsigned int _caca_rendertime;
103
104#if defined(OPTIMISE_SLANG_PALETTE)
105static int _caca_fgisbg = 0;
106#endif
107static enum caca_color _caca_fgcolor = CACA_COLOR_LIGHTGRAY;
108static enum caca_color _caca_bgcolor = CACA_COLOR_BLACK;
109
110/*
111 * Local functions
112 */
113#if defined(USE_SLANG)
114static void slang_init_palette(void);
115#endif
116
117static unsigned int _caca_getticks(void);
118
119/** \brief Set the default colour pair.
120 *
121 *  This function sets the default colour pair. String functions such as
122 *  caca_printf() and graphical primitive functions such as caca_draw_line()
123 *  will use these colour pairs.
124 *
125 *  \param fgcolor The requested foreground colour.
126 *  \param bgcolor The requested background colour.
127 */
128void caca_set_color(enum caca_color fgcolor, enum caca_color bgcolor)
129{
130    if(fgcolor < 0 || fgcolor > 15 || bgcolor < 0 || bgcolor > 15)
131        return;
132
133    _caca_fgcolor = fgcolor;
134    _caca_bgcolor = bgcolor;
135
136    switch(_caca_driver)
137    {
138#if defined(USE_SLANG)
139    case CACA_DRIVER_SLANG:
140
141#if defined(OPTIMISE_SLANG_PALETTE)
142        /* If foreground == background, discard this colour pair. Functions
143         * such as caca_putchar will print spaces instead of characters */
144        if(fgcolor != bgcolor)
145            _caca_fgisbg = 0;
146        else
147        {
148            _caca_fgisbg = 1;
149            switch(fgcolor)
150            {
151            case CACA_COLOR_BLACK:
152                fgcolor = CACA_COLOR_WHITE;
153                break;
154            case CACA_COLOR_WHITE:
155                fgcolor = CACA_COLOR_BLACK;
156                break;
157            default:
158                if(fgcolor <= CACA_COLOR_LIGHTGRAY)
159                    fgcolor = CACA_COLOR_BLACK;
160                else
161                    fgcolor = CACA_COLOR_WHITE;
162            }
163        }
164#endif
165
166#if defined(OPTIMISE_SLANG_PALETTE)
167        SLsmg_set_color(slang_assoc[fgcolor + 16 * bgcolor]);
168#else
169        SLsmg_set_color(fgcolor + 16 * bgcolor);
170#endif
171        break;
172#endif
173#if defined(USE_NCURSES)
174    case CACA_DRIVER_NCURSES:
175        attrset(ncurses_attr[fgcolor + 16 * bgcolor]);
176        break;
177#endif
178#if defined(USE_CONIO)
179    case CACA_DRIVER_CONIO:
180        textbackground(bgcolor);
181        textcolor(fgcolor);
182        break;
183#endif
184#if defined(USE_X11)
185    case CACA_DRIVER_X11:
186        /* FIXME */
187        break;
188#endif
189    default:
190        break;
191    }
192}
193
194/** \brief Get the current foreground colour.
195 *
196 *  This function returns the current foreground colour that was set with
197 *  caca_set_color().
198 *
199 *  \return The current foreground colour.
200 */
201enum caca_color caca_get_fg_color(void)
202{
203    return _caca_fgcolor;
204}
205
206/** \brief Get the current background colour.
207 *
208 *  This function returns the current background colour that was set with
209 *  caca_set_color().
210 *
211 *  \return The current background colour.
212 */
213enum caca_color caca_get_bg_color(void)
214{
215    return _caca_bgcolor;
216}
217
218/** \brief Print a character.
219 *
220 *  This function prints a character at the given coordinates, using the
221 *  default foreground and background values. If the coordinates are outside
222 *  the screen boundaries, nothing is printed.
223 *
224 *  \param x X coordinate.
225 *  \param y Y coordinate.
226 *  \param c The character to print.
227 */
228void caca_putchar(int x, int y, char c)
229{
230#if defined(USE_CONIO)
231    char *data;
232#endif
233    if(x < 0 || x >= (int)_caca_width ||
234       y < 0 || y >= (int)_caca_height)
235        return;
236
237    switch(_caca_driver)
238    {
239#if defined(USE_SLANG)
240    case CACA_DRIVER_SLANG:
241        SLsmg_gotorc(y, x);
242#if defined(OPTIMISE_SLANG_PALETTE)
243        if(_caca_fgisbg)
244            SLsmg_write_char(' ');
245        else
246#endif
247            SLsmg_write_char(c);
248        break;
249#endif
250#if defined(USE_NCURSES)
251    case CACA_DRIVER_NCURSES:
252        move(y, x);
253        addch(c);
254        break;
255#endif
256#if defined(USE_CONIO)
257    case CACA_DRIVER_CONIO:
258        data = conio_screen + 2 * (x + y * _caca_width);
259        data[0] = c;
260        data[1] = (_caca_bgcolor << 4) | _caca_fgcolor;
261//        gotoxy(x + 1, y + 1);
262//        putch(c);
263        break;
264#endif
265#if defined(USE_X11)
266    case CACA_DRIVER_X11:
267        x11_screen[x + y * _caca_width] =
268            ((int)c << 8) | ((int)_caca_bgcolor << 4) | (int)_caca_fgcolor;
269        break;
270#endif
271    default:
272        break;
273    }
274}
275
276/** \brief Print a string.
277 *
278 *  This function prints a string at the given coordinates, using the
279 *  default foreground and background values. The coordinates may be outside
280 *  the screen boundaries (eg. a negative Y coordinate) and the string will
281 *  be cropped accordingly if it is too long.
282 *
283 *  \param x X coordinate.
284 *  \param y Y coordinate.
285 *  \param s The string to print.
286 */
287void caca_putstr(int x, int y, const char *s)
288{
289#if defined(USE_CONIO)
290    char *charbuf;
291#endif
292#if defined(USE_X11)
293    int *intbuf;
294#endif
295    unsigned int len;
296
297    if(y < 0 || y >= (int)_caca_height || x >= (int)_caca_width)
298        return;
299
300    len = strlen(s);
301
302    if(x < 0)
303    {
304        len -= -x;
305        if(len < 0)
306            return;
307        s += -x;
308        x = 0;
309    }
310
311    if(x + len >= _caca_width)
312    {
313        len = _caca_width - x;
314        memcpy(_caca_scratch_line, s, len);
315        _caca_scratch_line[len] = '\0';
316        s = _caca_scratch_line;
317    }
318
319    switch(_caca_driver)
320    {
321#if defined(USE_SLANG)
322    case CACA_DRIVER_SLANG:
323        SLsmg_gotorc(y, x);
324#if defined(OPTIMISE_SLANG_PALETTE)
325        if(_caca_fgisbg)
326            SLsmg_write_string(_caca_empty_line + _caca_width - len);
327        else
328#endif
329            SLsmg_write_string((char *)(intptr_t)s);
330        break;
331#endif
332#if defined(USE_NCURSES)
333    case CACA_DRIVER_NCURSES:
334        move(y, x);
335        addstr(s);
336        break;
337#endif
338#if defined(USE_CONIO)
339    case CACA_DRIVER_CONIO:
340        charbuf = conio_screen + 2 * (x + y * _caca_width);
341        while(*s)
342        {
343            *charbuf++ = *s++;
344            *charbuf++ = (_caca_bgcolor << 4) | _caca_fgcolor;
345        }
346//        gotoxy(x + 1, y + 1);
347//        cputs(s);
348        break;
349#endif
350#if defined(USE_X11)
351    case CACA_DRIVER_X11:
352        intbuf = x11_screen + x + y * _caca_width;
353        while(*s)
354            *intbuf++ = ((int)*s++ << 8)
355                         | ((int)_caca_bgcolor << 4) | (int)_caca_fgcolor;
356        break;
357#endif
358    default:
359        break;
360    }
361}
362
363/** \brief Format a string.
364 *
365 *  This function formats a string at the given coordinates, using the
366 *  default foreground and background values. The coordinates may be outside
367 *  the screen boundaries (eg. a negative Y coordinate) and the string will
368 *  be cropped accordingly if it is too long. The syntax of the format
369 *  string is the same as for the C printf() function.
370 *
371 *  \param x X coordinate.
372 *  \param y Y coordinate.
373 *  \param format The format string to print.
374 *  \param ... Arguments to the format string.
375 */
376void caca_printf(int x, int y, const char *format, ...)
377{
378    char tmp[BUFSIZ];
379    char *buf = tmp;
380    va_list args;
381
382    if(y < 0 || y >= (int)_caca_height || x >= (int)_caca_width)
383        return;
384
385    if(_caca_width - x + 1 > BUFSIZ)
386        buf = malloc(_caca_width - x + 1);
387
388    va_start(args, format);
389#if defined(HAVE_VSNPRINTF)
390    vsnprintf(buf, _caca_width - x + 1, format, args);
391#else
392    vsprintf(buf, format, args);
393#endif
394    buf[_caca_width - x] = '\0';
395    va_end(args);
396
397    caca_putstr(x, y, buf);
398
399    if(buf != tmp)
400        free(buf);
401}
402
403/** \brief Clear the screen.
404 *
405 *  This function clears the screen using a black background.
406 */
407void caca_clear(void)
408{
409    enum caca_color oldfg = caca_get_fg_color();
410    enum caca_color oldbg = caca_get_bg_color();
411    int y = _caca_height;
412
413    caca_set_color(CACA_COLOR_LIGHTGRAY, CACA_COLOR_BLACK);
414
415    /* We could use SLsmg_cls() etc., but drawing empty lines is much faster */
416    while(y--)
417        caca_putstr(0, y, _caca_empty_line);
418
419    caca_set_color(oldfg, oldbg);
420}
421
422int _caca_init_graphics(void)
423{
424#if defined(USE_SLANG)
425    if(_caca_driver == CACA_DRIVER_SLANG)
426    {
427        slang_init_palette();
428
429        /* Disable alt charset support so that we get all 256 colour pairs */
430        SLtt_Has_Alt_Charset = 0;
431
432        _caca_width = SLtt_Screen_Cols;
433        _caca_height = SLtt_Screen_Rows;
434    }
435    else
436#endif
437#if defined(USE_NCURSES)
438    if(_caca_driver == CACA_DRIVER_NCURSES)
439    {
440        static int curses_colors[] =
441        {
442            /* Standard curses colours */
443            COLOR_BLACK,
444            COLOR_BLUE,
445            COLOR_GREEN,
446            COLOR_CYAN,
447            COLOR_RED,
448            COLOR_MAGENTA,
449            COLOR_YELLOW,
450            COLOR_WHITE,
451            /* Extra values for xterm-16color */
452            COLOR_BLACK + 8,
453            COLOR_BLUE + 8,
454            COLOR_GREEN + 8,
455            COLOR_CYAN + 8,
456            COLOR_RED + 8,
457            COLOR_MAGENTA + 8,
458            COLOR_YELLOW + 8,
459            COLOR_WHITE + 8
460        };
461
462        int fg, bg, max;
463
464        /* Activate colour */
465        start_color();
466
467        /* If COLORS == 16, it means the terminal supports full bright colours
468         * using setab and setaf (will use \e[90m \e[91m etc. for colours >= 8),
469         * we can build 16*16 colour pairs.
470         * If COLORS == 8, it means the terminal does not know about bright
471         * colours and we need to get them through A_BOLD and A_BLINK (\e[1m
472         * and \e[5m). We can only build 8*8 colour pairs. */
473        max = COLORS >= 16 ? 16 : 8;
474
475        for(bg = 0; bg < max; bg++)
476            for(fg = 0; fg < max; fg++)
477            {
478                /* Use ((max + 7 - fg) % max) instead of fg so that colour 0
479                 * is light gray on black, since some terminals don't like
480                 * this colour pair to be redefined. */
481                int col = ((max + 7 - fg) % max) + max * bg;
482                init_pair(col, curses_colors[fg], curses_colors[bg]);
483                ncurses_attr[fg + 16 * bg] = COLOR_PAIR(col);
484
485                if(max == 8)
486                {
487                    /* Bright fg on simple bg */
488                    ncurses_attr[fg + 8 + 16 * bg] = A_BOLD | COLOR_PAIR(col);
489                    /* Simple fg on bright bg */
490                    ncurses_attr[fg + 16 * (bg + 8)] = A_BLINK
491                                                        | COLOR_PAIR(col);
492                    /* Bright fg on bright bg */
493                    ncurses_attr[fg + 8 + 16 * (bg + 8)] = A_BLINK | A_BOLD
494                                                            | COLOR_PAIR(col);
495                }
496            }
497
498        _caca_width = COLS;
499        _caca_height = LINES;
500    }
501    else
502#endif
503#if defined(USE_CONIO)
504    if(_caca_driver == CACA_DRIVER_CONIO)
505    {
506        gettextinfo(&conio_ti);
507        conio_screen = malloc(2 * conio_ti.screenwidth
508                                 * conio_ti.screenheight * sizeof(char));
509        if(conio_screen == NULL)
510            return -1;
511#   if defined(SCREENUPDATE_IN_PC_H)
512        ScreenRetrieve(conio_screen);
513#   else
514        /* FIXME */
515#   endif
516        _caca_width = conio_ti.screenwidth;
517        _caca_height = conio_ti.screenheight;
518    }
519    else
520#endif
521#if defined(USE_X11)
522    if(_caca_driver == CACA_DRIVER_X11)
523    {
524        static int x11_palette[] =
525        {
526            /* Standard curses colours */
527            0, 0, 0,
528            0, 0, 32768,
529            0, 32768, 0,
530            0, 32768, 32768,
531            32768, 0, 0,
532            32768, 0, 32768,
533            32768, 32768, 0,
534            32768, 32768, 32768,
535            /* Extra values for xterm-16color */
536            16384, 16384, 16384,
537            16384, 16384, 65535,
538            16384, 65535, 16384,
539            16384, 65535, 65535,
540            65535, 16384, 16384,
541            65535, 16384, 65535,
542            65535, 65535, 16384,
543            65535, 65535, 65535,
544        };
545
546        Colormap colormap;
547        XSetWindowAttributes x11_attr;
548        const char *font_name = "8x13bold";
549        int i;
550
551        if(getenv("CACA_GEOMETRY"))
552            sscanf(getenv("CACA_GEOMETRY"),
553                   "%ux%u", &_caca_width, &_caca_height);
554
555        if(!_caca_width)
556            _caca_width = 80;
557        if(!_caca_height)
558            _caca_height = 32;
559
560        x11_screen = malloc(_caca_width * _caca_height * sizeof(int));
561        if(x11_screen == NULL)
562            return -1;
563
564        x11_dpy = XOpenDisplay(NULL);
565        if(x11_dpy == NULL)
566        {
567            free(x11_screen);
568            return -1;
569        }
570
571        if(getenv("CACA_FONT"))
572            font_name = getenv("CACA_FONT");
573
574        x11_font = XLoadFont(x11_dpy, font_name);
575        if(!x11_font)
576        {
577            XCloseDisplay(x11_dpy);
578            free(x11_screen);
579            return -1;
580        }
581
582        x11_font_struct = XQueryFont(x11_dpy, x11_font);
583        if(!x11_font_struct)
584        {
585            XUnloadFont(x11_dpy, x11_font);
586            XCloseDisplay(x11_dpy);
587            free(x11_screen);
588            return -1;
589        }
590
591        x11_font_width = x11_font_struct->max_bounds.width;
592        x11_font_height = x11_font_struct->max_bounds.ascent
593                             + x11_font_struct->max_bounds.descent;
594        x11_font_offset = x11_font_struct->max_bounds.descent;
595
596        colormap = DefaultColormap(x11_dpy, DefaultScreen(x11_dpy));
597        for(i = 0; i < 16; i++)
598        {
599            XColor color;
600            color.red = x11_palette[i * 3];
601            color.green = x11_palette[i * 3 + 1];
602            color.blue = x11_palette[i * 3 + 2];
603            XAllocColor(x11_dpy, colormap, &color);
604            x11_colors[i] = color.pixel;
605        }
606
607        x11_attr.backing_store = Always;
608        x11_attr.background_pixel = x11_colors[0];
609        x11_attr.event_mask = ExposureMask | StructureNotifyMask;
610
611        x11_window = XCreateWindow(x11_dpy, DefaultRootWindow(x11_dpy), 0, 0,
612                                   _caca_width * x11_font_width,
613                                   _caca_height * x11_font_height,
614                                   0, 0, InputOutput, 0,
615                                   CWBackingStore | CWBackPixel | CWEventMask,
616                                   &x11_attr);
617
618        XStoreName(x11_dpy, x11_window, "caca for X");
619
620        XSelectInput(x11_dpy, x11_window, StructureNotifyMask);
621        XMapWindow(x11_dpy, x11_window);
622
623        x11_gc = XCreateGC(x11_dpy, x11_window, 0, NULL);
624        XSetForeground(x11_dpy, x11_gc, x11_colors[15]);
625        XSetFont(x11_dpy, x11_gc, x11_font);
626
627        for(;;)
628        {
629            XEvent event;
630            XNextEvent(x11_dpy, &event);
631            if (event.type == MapNotify)
632                break;
633        }
634
635        XSelectInput(x11_dpy, x11_window,
636                     KeyPressMask | ButtonPressMask | PointerMotionMask);
637
638        XSync(x11_dpy, False);
639
640        x11_pixmap = XCreatePixmap(x11_dpy, x11_window,
641                                   _caca_width * x11_font_width,
642                                   _caca_height * x11_font_height,
643                                   DefaultDepth(x11_dpy,
644                                                DefaultScreen(x11_dpy)));
645    }
646#endif
647
648    _caca_empty_line = malloc(_caca_width + 1);
649    memset(_caca_empty_line, ' ', _caca_width);
650    _caca_empty_line[_caca_width] = '\0';
651
652    _caca_scratch_line = malloc(_caca_width + 1);
653
654    _caca_delay = 0;
655    _caca_rendertime = 0;
656
657    return 0;
658}
659
660int _caca_end_graphics(void)
661{
662#if defined(USE_SLANG)
663    /* Nothing to do */
664#endif
665#if defined(USE_NCURSES)
666    /* Nothing to do */
667#endif
668#if defined(USE_CONIO)
669    if(_caca_driver == CACA_DRIVER_CONIO)
670    {
671        free(conio_screen);
672    }
673    else
674#endif
675#if defined(USE_X11)
676    if(_caca_driver == CACA_DRIVER_X11)
677    {
678        XSync(x11_dpy, False);
679        XFreePixmap(x11_dpy, x11_pixmap);
680        XFreeFont(x11_dpy, x11_font_struct);
681        XFreeGC(x11_dpy, x11_gc);
682        XUnmapWindow(x11_dpy, x11_window);
683        XDestroyWindow(x11_dpy, x11_window);
684        XCloseDisplay(x11_dpy);
685        free(x11_screen);
686    }
687#endif
688    free(_caca_empty_line);
689
690    return 0;
691}
692
693/** \brief Set the refresh delay.
694 *
695 *  This function sets the refresh delay in microseconds. The refresh delay
696 *  is used by caca_refresh() to achieve constant framerate. See the
697 *  caca_refresh() documentation for more details.
698 *
699 *  If the argument is zero, constant framerate is disabled. This is the
700 *  default behaviour.
701 *
702 *  \param usec The refresh delay in microseconds.
703 */
704void caca_set_delay(unsigned int usec)
705{
706    _caca_delay = usec;
707}
708
709/** \brief Get the average rendering time.
710 *
711 *  This function returns the average rendering time, which is the average
712 *  measured time between two caca_refresh() calls, in microseconds. If
713 *  constant framerate was activated by calling caca_set_delay(), the average
714 *  rendering time will not be considerably shorter than the requested delay
715 *  even if the real rendering time was shorter.
716 *
717 *  \return The render time in microseconds.
718 */
719unsigned int caca_get_rendertime(void)
720{
721    return _caca_rendertime;
722}
723
724static unsigned int _caca_getticks(void)
725{
726    static unsigned int last_sec = 0, last_usec = 0;
727
728    struct timeval tv;
729    unsigned int ticks = 0;
730
731    gettimeofday(&tv, NULL);
732
733    if(last_sec != 0)
734    {
735        ticks = (tv.tv_sec - last_sec) * 1000000 + (tv.tv_usec - last_usec);
736    }
737
738    last_sec = tv.tv_sec;
739    last_usec = tv.tv_usec;
740
741    return ticks;
742}
743
744/** \brief Flush pending changes and redraw the screen.
745 *
746 *  This function flushes all graphical operations and prints them to the
747 *  screen. Nothing will show on the screen caca_refresh() is not called.
748 *
749 *  If caca_set_delay() was called with a non-zero value, caca_refresh()
750 *  will use that value to achieve constant framerate: if two consecutive
751 *  calls to caca_refresh() are within a time range shorter than the value
752 *  set with caca_set_delay(), the second call will wait a bit before
753 *  performing the screen refresh.
754 */
755void caca_refresh(void)
756{
757#define IDLE_USEC 10000
758    static int lastticks = 0;
759    int ticks = lastticks + _caca_getticks();
760
761#if defined(USE_SLANG)
762    if(_caca_driver == CACA_DRIVER_SLANG)
763    {
764        SLsmg_refresh();
765    }
766    else
767#endif
768#if defined(USE_NCURSES)
769    if(_caca_driver == CACA_DRIVER_NCURSES)
770    {
771        refresh();
772    }
773    else
774#endif
775#if defined(USE_CONIO)
776    if(_caca_driver == CACA_DRIVER_CONIO)
777    {
778#   if defined(SCREENUPDATE_IN_PC_H)
779        ScreenUpdate(conio_screen);
780#   else
781        /* FIXME */
782#   endif
783    }
784    else
785#endif
786#if defined(USE_X11)
787    if(_caca_driver == CACA_DRIVER_X11)
788    {
789        unsigned int x, y;
790
791        /* FIXME: This is very, very slow. There are several things that can
792         * be done in order to speed up things:
793         *  - cache characters once rendered
794         *  - pre-render all characters
795         *  - use our own rendering routine (screen depth dependent) */
796        for(y = 0; y < _caca_height; y++)
797            for(x = 0; x < _caca_width; x++)
798            {
799                int item = x11_screen[x + y * _caca_width];
800                char data = item >> 8;
801                XSetForeground(x11_dpy, x11_gc, x11_colors[(item >> 4) & 0xf]);
802                XFillRectangle(x11_dpy, x11_pixmap, x11_gc,
803                               x * x11_font_width, y * x11_font_height,
804                               x11_font_width, x11_font_height);
805                XSetForeground(x11_dpy, x11_gc, x11_colors[item & 0xf]);
806                XDrawString(x11_dpy, x11_pixmap, x11_gc, x * x11_font_width,
807                            (y + 1) * x11_font_height - x11_font_offset,
808                            &data, 1);
809            }
810        XCopyArea(x11_dpy, x11_pixmap, x11_window, x11_gc, 0, 0,
811                  _caca_width * x11_font_width, _caca_height * x11_font_height,
812                  0, 0);
813        XFlush(x11_dpy);
814    }
815#endif
816
817    /* Wait until _caca_delay + time of last call */
818    ticks += _caca_getticks();
819    for(; ticks + IDLE_USEC < (int)_caca_delay; ticks += _caca_getticks())
820        usleep(IDLE_USEC);
821
822    /* Update the sliding mean of the render time */
823    _caca_rendertime = (7 * _caca_rendertime + ticks) / 8;
824
825    lastticks = ticks - _caca_delay;
826
827    /* If we drifted too much, it's bad, bad, bad. */
828    if(lastticks > (int)_caca_delay)
829        lastticks = 0;
830}
831
832#if defined(USE_SLANG)
833
834#if defined(OPTIMISE_SLANG_PALETTE)
835#endif
836
837static void slang_init_palette(void)
838{
839    /* See SLang ref., 5.4.4. */
840    static char *slang_colors[16] =
841    {
842        /* Standard colours */
843        "black",
844        "blue",
845        "green",
846        "cyan",
847        "red",
848        "magenta",
849        "brown",
850        "lightgray",
851        /* Bright colours */
852        "gray",
853        "brightblue",
854        "brightgreen",
855        "brightcyan",
856        "brightred",
857        "brightmagenta",
858        "yellow",
859        "white",
860    };
861
862#if !defined(OPTIMISE_SLANG_PALETTE)
863    int fg, bg;
864
865    for(bg = 0; bg < 16; bg++)
866        for(fg = 0; fg < 16; fg++)
867        {
868            int i = fg + 16 * bg;
869            SLtt_set_color(i, NULL, slang_colors[fg], slang_colors[bg]);
870        }
871#else
872    int i, cur = 0;
873
874    /* 6 colours in hue order */
875    static const enum caca_color hue_list[] =
876    {
877        CACA_COLOR_RED,
878        CACA_COLOR_BROWN,
879        CACA_COLOR_GREEN,
880        CACA_COLOR_CYAN,
881        CACA_COLOR_BLUE,
882        CACA_COLOR_MAGENTA
883    };
884
885#define SETPAIR(_fg, _bg, _n) \
886    do \
887    { \
888        int fg = _fg, bg = _bg, n = _n; \
889        SLtt_set_color(n, NULL, slang_colors[fg], slang_colors[bg]); \
890        slang_assoc[fg + 16 * bg] = n; \
891    } \
892    while(0);
893
894    /*
895     * XXX: See the NOTES file for what follows
896     */
897
898    /* black background colour pairs that are needed for the old renderer */
899    for(i = 1; i < 16; i++)
900        SETPAIR(i, CACA_COLOR_BLACK, cur++);
901
902    /* gray combinations used for grayscale dithering */
903    SETPAIR(CACA_COLOR_BLACK, CACA_COLOR_DARKGRAY, cur++);
904    SETPAIR(CACA_COLOR_DARKGRAY, CACA_COLOR_LIGHTGRAY, cur++);
905    SETPAIR(CACA_COLOR_LIGHTGRAY, CACA_COLOR_DARKGRAY, cur++);
906    SETPAIR(CACA_COLOR_WHITE, CACA_COLOR_LIGHTGRAY, cur++);
907    SETPAIR(CACA_COLOR_LIGHTGRAY, CACA_COLOR_WHITE, cur++);
908
909    /* white/light, light/dark, lightgray/light, darkgray/dark, dark/black
910     * combinations often used for saturation/value dithering (the two
911     * other possible combinations, lightgray/dark and darkgray/light, are
912     * not considered here) */
913    for(i = 1; i < 7; i++)
914    {
915        SETPAIR(CACA_COLOR_WHITE, i + 8, cur++);
916        SETPAIR(i + 8, CACA_COLOR_WHITE, cur++);
917        SETPAIR(i, i + 8, cur++);
918        SETPAIR(i + 8, i, cur++);
919        SETPAIR(CACA_COLOR_LIGHTGRAY, i + 8, cur++);
920        SETPAIR(i + 8, CACA_COLOR_LIGHTGRAY, cur++);
921        SETPAIR(CACA_COLOR_DARKGRAY, i, cur++);
922        SETPAIR(i, CACA_COLOR_DARKGRAY, cur++);
923        SETPAIR(CACA_COLOR_BLACK, i, cur++);
924    }
925
926    /* next colour combinations for hue dithering (magenta/blue, blue/green
927     * and so on) */
928    for(i = 0; i < 6; i++)
929    {
930        SETPAIR(hue_list[i], hue_list[(i + 1) % 6], cur++);
931        SETPAIR(hue_list[(i + 1) % 6], hue_list[i], cur++);
932        SETPAIR(hue_list[i] + 8, hue_list[(i + 1) % 6] + 8, cur++);
933        SETPAIR(hue_list[(i + 1) % 6] + 8, hue_list[i] + 8, cur++);
934    }
935
936    /* next colour combinations for hue/value dithering (blue/lightgreen,
937     * green/lightblue and so on) */
938    for(i = 0; i < 6; i++)
939    {
940        SETPAIR(hue_list[i], hue_list[(i + 1) % 6] + 8, cur++);
941        SETPAIR(hue_list[(i + 1) % 6], hue_list[i] + 8, cur++);
942        SETPAIR(hue_list[i] + 8, hue_list[(i + 1) % 6], cur++);
943        SETPAIR(hue_list[(i + 1) % 6] + 8, hue_list[i], cur++);
944    }
945
946    /* black on light gray, black on white, white on dark gray, dark gray
947     * on white, white on blue, light gray on blue (chosen arbitrarily) */
948    SETPAIR(CACA_COLOR_BLACK, CACA_COLOR_LIGHTGRAY, cur++);
949    SETPAIR(CACA_COLOR_BLACK, CACA_COLOR_WHITE, cur++);
950    SETPAIR(CACA_COLOR_WHITE, CACA_COLOR_DARKGRAY, cur++);
951    SETPAIR(CACA_COLOR_DARKGRAY, CACA_COLOR_WHITE, cur++);
952    SETPAIR(CACA_COLOR_WHITE, CACA_COLOR_BLUE, cur++);
953    SETPAIR(CACA_COLOR_LIGHTGRAY, CACA_COLOR_BLUE, cur++);
954
955    /*
956     * Now the possibly emulated pairs
957     */
958
959    /* light gray on dark colour: emulate with light colour on dark colour
960     * white on dark colour: emulate with light gray on light colour
961     * black on light colour: emulate with dark gray on dark colour
962     * dark gray on light colour: emulate with dark colour on light colour
963     * light colour on dark gray: emulate with dark colour on dark gray
964     * dark colour on light gray: emulate with light colour on light gray
965     * dark colour on white: emulate with light colour on white */
966    for(i = 1; i < 7; i++)
967    {
968        if(i != CACA_COLOR_BLUE)
969        {
970            SETPAIR(CACA_COLOR_LIGHTGRAY, i, 128 +
971                    slang_assoc[i + 8 + 16 * i]);
972            SETPAIR(CACA_COLOR_WHITE, i, 128 +
973                    slang_assoc[CACA_COLOR_LIGHTGRAY + 16 * (i + 8)]);
974        }
975        SETPAIR(CACA_COLOR_BLACK, i + 8,
976                128 + slang_assoc[CACA_COLOR_DARKGRAY + 16 * i]);
977        SETPAIR(CACA_COLOR_DARKGRAY, i + 8,
978                128 + slang_assoc[i + 16 * (i + 8)]);
979        SETPAIR(i + 8, CACA_COLOR_DARKGRAY,
980                128 + slang_assoc[i + 16 * CACA_COLOR_DARKGRAY]);
981        SETPAIR(i, CACA_COLOR_LIGHTGRAY,
982                128 + slang_assoc[i + 8 + 16 * CACA_COLOR_LIGHTGRAY]);
983        SETPAIR(i, CACA_COLOR_WHITE,
984                128 + slang_assoc[i + 8 + 16 * CACA_COLOR_WHITE]);
985    }
986
987    /* 120 degree hue pairs can be emulated as well; for instance blue on
988     * red can be emulated using magenta on red, and blue on green using
989     * cyan on green */
990    for(i = 0; i < 6; i++)
991    {
992        SETPAIR(hue_list[(i + 2) % 6], hue_list[i],
993            128 + slang_assoc[hue_list[(i + 1) % 6] + 16 * hue_list[i]]);
994        SETPAIR(hue_list[(i + 2) % 6] + 8, hue_list[i] + 8,
995            128 + slang_assoc[hue_list[(i + 1) % 6] + 16 * hue_list[i] + 136]);
996        SETPAIR(hue_list[(i + 2) % 6] + 8, hue_list[i],
997            128 + slang_assoc[hue_list[(i + 1) % 6] + 16 * hue_list[i] + 8]);
998        SETPAIR(hue_list[(i + 2) % 6], hue_list[i] + 8,
999            128 + slang_assoc[hue_list[(i + 1) % 6] + 16 * hue_list[i] + 128]);
1000
1001        SETPAIR(hue_list[(i + 4) % 6], hue_list[i],
1002            128 + slang_assoc[hue_list[(i + 5) % 6] + 16 * hue_list[i]]);
1003        SETPAIR(hue_list[(i + 4) % 6] + 8, hue_list[i] + 8,
1004            128 + slang_assoc[hue_list[(i + 5) % 6] + 16 * hue_list[i] + 136]);
1005        SETPAIR(hue_list[(i + 4) % 6] + 8, hue_list[i],
1006            128 + slang_assoc[hue_list[(i + 5) % 6] + 16 * hue_list[i] + 8]);
1007        SETPAIR(hue_list[(i + 4) % 6], hue_list[i] + 8,
1008            128 + slang_assoc[hue_list[(i + 5) % 6] + 16 * hue_list[i] + 128]);
1009    }
1010
1011    /* dark opposite on dark: emulate with dark opposite on black
1012     * light opposite on dark: emulate with light opposite on black
1013     * dark opposite on light: emulate with black on dark
1014     * light opposite on light: emulate with white on light */
1015    for(i = 0; i < 6; i++)
1016    {
1017        SETPAIR(hue_list[i], hue_list[(i + 3) % 6],
1018                128 + slang_assoc[hue_list[i] + 16 * CACA_COLOR_BLACK]);
1019        SETPAIR(hue_list[i] + 8, hue_list[(i + 3) % 6],
1020                128 + slang_assoc[hue_list[i] + 8 + 16 * CACA_COLOR_BLACK]);
1021        SETPAIR(hue_list[(i + 3) % 6], hue_list[i] + 8,
1022                128 + slang_assoc[CACA_COLOR_BLACK + 16 * hue_list[i]]);
1023        SETPAIR(hue_list[(i + 3) % 6] + 8, hue_list[i] + 8,
1024                128 + slang_assoc[CACA_COLOR_WHITE + 16 * (hue_list[i] + 8)]);
1025    }
1026#endif
1027}
1028#endif /* USE_SLANG */
1029
Note: See TracBrowser for help on using the repository browser.