source: libcaca/trunk/cucul/cucul.c @ 553

Last change on this file since 553 was 553, checked in by Sam Hocevar, 15 years ago
  • Fixed async issues between the driver and libcaca when resizing windows, and simplified the handle_resize() API. Still can be polished.
  • Property svn:keywords set to Id
File size: 12.0 KB
Line 
1/*
2 *  libcucul      Unicode canvas library
3 *  Copyright (c) 2002-2006 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 Do What The Fuck You Want To
8 *  Public License, Version 2, as published by Sam Hocevar. See
9 *  http://sam.zoy.org/wtfpl/COPYING for more details.
10 */
11
12/** \file cucul.c
13 *  \version \$Id: cucul.c 553 2006-03-08 19:41:53Z sam $
14 *  \author Sam Hocevar <sam@zoy.org>
15 *  \brief Main \e libcucul functions
16 *
17 *  This file contains the main functions used by \e libcucul applications
18 *  to initialise a drawing context.
19 */
20
21#include "config.h"
22
23#include <stdlib.h>
24#include <string.h>
25
26#include "cucul.h"
27#include "cucul_internals.h"
28
29static void cucul_read_environment(cucul_t *);
30void _cucul_set_size(cucul_t *, unsigned int, unsigned int);
31
32/** \brief Initialise \e libcucul.
33 *
34 *  This function initialises internal \e libcucul structures and the backend
35 *  that will be used for subsequent graphical operations. It must be the
36 *  first \e libcucul function to be called in a function. cucul_end() should
37 *  be called at the end of the program to free all allocated resources.
38 *
39 *  \return 0 upon success, a non-zero value if an error occurs.
40 */
41cucul_t * cucul_init(void)
42{
43    cucul_t *qq = malloc(sizeof(cucul_t));
44    int i;
45
46    cucul_read_environment(qq);
47
48    qq->fgcolor = CUCUL_COLOR_LIGHTGRAY;
49    qq->bgcolor = CUCUL_COLOR_BLACK;
50
51    /* Initialise to a default size. When a graphic driver attaches to
52     * us, we'll adapt to its size. */
53    qq->width = 80;
54    qq->height = 32;
55
56    qq->chars = malloc(qq->width * qq->height * sizeof(uint32_t));
57    qq->attr = malloc(qq->width * qq->height * sizeof(uint8_t));
58
59    for(i = qq->width * qq->height; i--; )
60        qq->chars[i] = (uint32_t)' ';
61    memset(qq->attr, 0, qq->width * qq->height * sizeof(uint8_t));
62
63    qq->empty_line = malloc(qq->width + 1);
64    memset(qq->empty_line, ' ', qq->width);
65    qq->empty_line[qq->width] = '\0';
66
67    qq->scratch_line = malloc(qq->width + 1);
68
69    if(_cucul_init_bitmap())
70    {
71        free(qq);
72        return NULL;
73    }
74
75    return qq;
76}
77
78/** \brief Set the canvas size.
79 *
80 *  This function sets the canvas width and height, in character cells.
81 *
82 *  It is an error to try to resize the canvas if an output driver has
83 *  been attached to the canvas using caca_attach(). You need to remove
84 *  the output driver using caca_detach() before you can change the
85 *  canvas size again.
86 *
87 *  However, the caca output driver can cause a canvas resize through
88 *  user interaction. See the caca_event() documentation for more about
89 *  this.
90 *
91 *  \param width The desired canvas width
92 *  \param height The desired canvas height
93 */
94void cucul_set_size(cucul_t *qq, unsigned int width, unsigned int height)
95{
96    if(qq->refcount)
97        return;
98
99    _cucul_set_size(qq, width, height);
100}
101
102/** \brief Get the canvas width.
103 *
104 *  This function returns the current canvas width, in character cells.
105 *
106 *  \return The canvas width.
107 */
108unsigned int cucul_get_width(cucul_t *qq)
109{
110    return qq->width;
111}
112
113/** \brief Get the canvas height.
114 *
115 *  This function returns the current canvas height, in character cells.
116 *
117 *  \return The canvas height.
118 */
119unsigned int cucul_get_height(cucul_t *qq)
120{
121    return qq->height;
122}
123
124/** \brief Translate a colour index into the colour's name.
125 *
126 *  This function translates a cucul_color enum into a human-readable
127 *  description string of the associated colour.
128 *
129 *  \param color The colour value.
130 *  \return A static string containing the colour's name.
131 */
132char const *cucul_get_color_name(enum cucul_color color)
133{
134    static char const *color_names[] =
135    {
136        "black",
137        "blue",
138        "green",
139        "cyan",
140        "red",
141        "magenta",
142        "brown",
143        "light gray",
144        "dark gray",
145        "light blue",
146        "light green",
147        "light cyan",
148        "light red",
149        "light magenta",
150        "yellow",
151        "white",
152    };
153
154    if(color < 0 || color > 15)
155        return "unknown";
156
157    return color_names[color];
158}
159
160/** \brief Get the current value of a feature.
161 *
162 *  This function retrieves the value of an internal \e libcucul feature. A
163 *  generic feature value is expected, such as CUCUL_ANTIALIASING.
164 *
165 *  \param feature The requested feature.
166 *  \return The current value of the feature or CUCUL_FEATURE_UNKNOWN if an
167 *          error occurred..
168 */
169enum cucul_feature cucul_get_feature(cucul_t *qq, enum cucul_feature feature)
170{
171    switch(feature)
172    {
173        case CUCUL_BACKGROUND:
174            return qq->background;
175        case CUCUL_ANTIALIASING:
176            return qq->antialiasing;
177        case CUCUL_DITHERING:
178            return qq->dithering;
179
180        default:
181            return CUCUL_FEATURE_UNKNOWN;
182    }
183}
184
185/** \brief Set a feature.
186 *
187 *  This function sets an internal \e libcucul feature such as the antialiasing
188 *  or dithering modes. If a specific feature such as CUCUL_DITHERING_RANDOM,
189 *  cucul_set_feature() will set it immediately. If a generic feature is given
190 *  instead, such as CUCUL_DITHERING, the default value will be used instead.
191 *
192 *  \param feature The requested feature.
193 */
194void cucul_set_feature(cucul_t *qq, enum cucul_feature feature)
195{
196    switch(feature)
197    {
198        case CUCUL_BACKGROUND:
199            feature = CUCUL_BACKGROUND_SOLID;
200        case CUCUL_BACKGROUND_BLACK:
201        case CUCUL_BACKGROUND_SOLID:
202            qq->background = feature;
203            break;
204
205        case CUCUL_ANTIALIASING:
206            feature = CUCUL_ANTIALIASING_PREFILTER;
207        case CUCUL_ANTIALIASING_NONE:
208        case CUCUL_ANTIALIASING_PREFILTER:
209            qq->antialiasing = feature;
210            break;
211
212        case CUCUL_DITHERING:
213            feature = CUCUL_DITHERING_FSTEIN;
214        case CUCUL_DITHERING_NONE:
215        case CUCUL_DITHERING_ORDERED2:
216        case CUCUL_DITHERING_ORDERED4:
217        case CUCUL_DITHERING_ORDERED8:
218        case CUCUL_DITHERING_RANDOM:
219        case CUCUL_DITHERING_FSTEIN:
220            qq->dithering = feature;
221            break;
222
223        case CUCUL_FEATURE_UNKNOWN:
224            break;
225    }
226}
227
228/** \brief Translate a feature value into the feature's name.
229 *
230 *  This function translates a cucul_feature enum into a human-readable
231 *  description string of the associated feature.
232 *
233 *  \param feature The feature value.
234 *  \return A static string containing the feature's name.
235 */
236char const *cucul_get_feature_name(enum cucul_feature feature)
237{
238    switch(feature)
239    {
240        case CUCUL_BACKGROUND_BLACK: return "black background";
241        case CUCUL_BACKGROUND_SOLID: return "solid background";
242
243        case CUCUL_ANTIALIASING_NONE:      return "no antialiasing";
244        case CUCUL_ANTIALIASING_PREFILTER: return "prefilter antialiasing";
245
246        case CUCUL_DITHERING_NONE:     return "no dithering";
247        case CUCUL_DITHERING_ORDERED2: return "2x2 ordered dithering";
248        case CUCUL_DITHERING_ORDERED4: return "4x4 ordered dithering";
249        case CUCUL_DITHERING_ORDERED8: return "8x8 ordered dithering";
250        case CUCUL_DITHERING_RANDOM:   return "random dithering";
251        case CUCUL_DITHERING_FSTEIN:   return "Floyd-Steinberg dithering";
252
253        default: return "unknown";
254    }
255}
256
257/** \brief Uninitialise \e libcucul.
258 *
259 *  This function frees all resources allocated by cucul_init(). After
260 *  cucul_end() has been called, no other \e libcucul functions may be used
261 *  unless a new call to cucul_init() is done.
262 */
263void cucul_end(cucul_t *qq)
264{
265    _cucul_end_bitmap();
266
267    free(qq->empty_line);
268    free(qq->scratch_line);
269
270    free(qq->chars);
271    free(qq->attr);
272
273    free(qq);
274}
275
276/*
277 * XXX: The following functions are local.
278 */
279
280static void cucul_read_environment(cucul_t * qq)
281{
282    /* FIXME: if strcasecmp isn't available, use strcmp */
283#if defined(HAVE_GETENV) && defined(HAVE_STRCASECMP)
284    char *var;
285#endif
286
287    cucul_set_feature(qq, CUCUL_BACKGROUND);
288    cucul_set_feature(qq, CUCUL_ANTIALIASING);
289    cucul_set_feature(qq, CUCUL_DITHERING);
290
291#if defined(HAVE_GETENV) && defined(HAVE_STRCASECMP)
292    if((var = getenv("CUCUL_BACKGROUND")) && *var)
293    {
294        if(!strcasecmp("black", var))
295            cucul_set_feature(qq, CUCUL_BACKGROUND_BLACK);
296        else if(!strcasecmp("solid", var))
297            cucul_set_feature(qq, CUCUL_BACKGROUND_SOLID);
298    }
299
300    if((var = getenv("CUCUL_ANTIALIASING")) && *var)
301    {
302        if(!strcasecmp("none", var))
303            cucul_set_feature(qq, CUCUL_ANTIALIASING_NONE);
304        else if(!strcasecmp("prefilter", var))
305            cucul_set_feature(qq, CUCUL_ANTIALIASING_PREFILTER);
306    }
307
308    if((var = getenv("CUCUL_DITHERING")) && *var)
309    {
310        if(!strcasecmp("none", var))
311            cucul_set_feature(qq, CUCUL_DITHERING_NONE);
312        else if(!strcasecmp("ordered2", var))
313            cucul_set_feature(qq, CUCUL_DITHERING_ORDERED2);
314        else if(!strcasecmp("ordered4", var))
315            cucul_set_feature(qq, CUCUL_DITHERING_ORDERED4);
316        else if(!strcasecmp("ordered8", var))
317            cucul_set_feature(qq, CUCUL_DITHERING_ORDERED8);
318        else if(!strcasecmp("random", var))
319            cucul_set_feature(qq, CUCUL_DITHERING_RANDOM);
320        else if(!strcasecmp("fstein", var))
321            cucul_set_feature(qq, CUCUL_DITHERING_FSTEIN);
322    }
323#endif
324}
325
326void _cucul_set_size(cucul_t *qq, unsigned int width, unsigned int height)
327{
328    unsigned int x, y, old_width, old_height, new_size, old_size;
329
330    old_width = qq->width;
331    old_height = qq->height;
332    old_size = old_width * old_height;
333
334    qq->width = width;
335    qq->height = height;
336    new_size = width * height;
337
338    /* Step 1: if new area is bigger, resize the memory area now. */
339    if(new_size > old_size)
340    {
341        qq->chars = realloc(qq->chars, new_size * sizeof(uint32_t));
342        qq->attr = realloc(qq->attr, new_size * sizeof(uint8_t));
343    }
344
345    /* Step 2: move line data if necessary. */
346    if(width == old_width)
347    {
348        /* Width did not change, which means we do not need to move data. */
349        ;
350    }
351    else if(width > old_width)
352    {
353        /* New width is bigger than old width, which means we need to
354         * copy lines starting from the bottom of the screen otherwise
355         * we will overwrite information. */
356        for(y = height < old_height ? height : old_height; y--; )
357        {
358            for(x = old_width; x--; )
359            {
360                qq->chars[y * width + x] = qq->chars[y * old_width + x];
361                qq->attr[y * width + x] = qq->attr[y * old_width + x];
362            }
363
364            /* Zero the end of the line */
365            for(x = width - old_width; x--; )
366                qq->chars[y * width + old_width + x] = (uint32_t)' ';
367            memset(qq->attr + y * width + old_width, 0,
368                   width - old_width);
369        }
370    }
371    else
372    {
373        /* New width is smaller. Copy as many lines as possible. Ignore
374         * the first line, it is already in place. */
375        unsigned int lines = height < old_height ? height : old_height;
376
377        for(y = 1; y < lines; y++)
378        {
379            for(x = 0; x < width; x++)
380            {
381                qq->chars[y * width + x] = qq->chars[y * old_width + x];
382                qq->attr[y * width + x] = qq->attr[y * old_width + x];
383            }
384        }
385    }
386
387    /* Step 3: fill the bottom of the new screen if necessary. */
388    if(height > old_height)
389    {
390        /* Zero the bottom of the screen */
391        for(x = (height - old_height) * width; x--; )
392            qq->chars[old_height * width + x] = (uint32_t)' ';
393        memset(qq->attr + old_height * width, 0,
394               (height - old_height) * width);
395    }
396
397    /* Step 4: if new area is smaller, resize memory area now. */
398    if(new_size <= old_size)
399    {
400        qq->chars = realloc(qq->chars, new_size * sizeof(uint32_t));
401        qq->attr = realloc(qq->attr, new_size * sizeof(uint8_t));
402    }
403
404    /* Recompute the scratch line and the empty line */
405    if(width != old_width)
406    {
407        qq->empty_line = realloc(qq->empty_line, width + 1);
408        memset(qq->empty_line, ' ', width);
409        qq->empty_line[width] = '\0';
410
411        qq->scratch_line = realloc(qq->scratch_line, width + 1);
412    }
413}
414
Note: See TracBrowser for help on using the repository browser.