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

Last change on this file since 874 was 874, checked in by Sam Hocevar, 15 years ago
  • More error checking in libcucul.
  • Property svn:keywords set to Id
File size: 11.1 KB
Line 
1/*
2 *  libcucul      Canvas for ultrafast compositing of Unicode letters
3 *  Copyright (c) 2002-2006 Sam Hocevar <sam@zoy.org>
4 *                All Rights Reserved
5 *
6 *  $Id: cucul.c 874 2006-04-25 11:05:25Z sam $
7 *
8 *  This library is free software; you can redistribute it and/or
9 *  modify it under the terms of the Do What The Fuck You Want To
10 *  Public License, Version 2, as published by Sam Hocevar. See
11 *  http://sam.zoy.org/wtfpl/COPYING for more details.
12 */
13
14/*
15 *  This file contains the main functions used by \e libcucul applications
16 *  to initialise a drawing context.
17 */
18
19#include "config.h"
20#include "common.h"
21
22#if !defined(__KERNEL__)
23#   include <stdio.h>
24#   include <stdlib.h>
25#   include <string.h>
26#   if defined(HAVE_ERRNO_H)
27#       include <errno.h>
28#   endif
29#endif
30
31#include "cucul.h"
32#include "cucul_internals.h"
33
34/** \brief Initialise a \e libcucul canvas.
35 *
36 *  This function initialises internal \e libcucul structures and the backend
37 *  that will be used for subsequent graphical operations. It must be the
38 *  first \e libcucul function to be called in a function. cucul_free_canvas()
39 *  should be called at the end of the program to free all allocated resources.
40 *
41 *  If one of the desired canvas coordinates is zero, a default canvas size
42 *  of 80x32 is used instead.
43 *
44 *  If an error occurs, -1 is returned and \b errno is set accordingly:
45 *  - \c ENOMEM Not enough memory for the requested canvas size.
46 *
47 *  \param width The desired canvas width
48 *  \param height The desired canvas height
49 *  \return A libcucul canvas handle upon success, NULL if an error occurred.
50 */
51cucul_canvas_t * cucul_create_canvas(unsigned int width, unsigned int height)
52{
53    cucul_canvas_t *cv = malloc(sizeof(cucul_canvas_t));
54    int ret;
55
56    if(!cv)
57        goto nomem;
58
59    cv->refcount = 0;
60
61    cv->fgcolor = CUCUL_COLOR_LIGHTGRAY;
62    cv->bgcolor = CUCUL_COLOR_BLACK;
63
64    cv->width = cv->height = 0;
65    cv->chars = NULL;
66    cv->attr = NULL;
67
68    cv->frame = 0;
69    cv->framecount = 1;
70    cv->allchars = malloc(sizeof(uint32_t *));
71    if(!cv->allchars)
72    {
73        free(cv);
74        goto nomem;
75    }
76    cv->allattr = malloc(sizeof(uint32_t *));
77    if(!cv->allattr)
78    {
79        free(cv->allchars);
80        free(cv);
81        goto nomem;
82    }
83    cv->allchars[0] = NULL;
84    cv->allattr[0] = NULL;
85
86    /* Initialise to a default size. 80x32 is arbitrary but matches AAlib's
87     * default X11 window. When a graphic driver attaches to us, it can set
88     * a different size. */
89    if(width && height)
90        ret = _cucul_set_canvas_size(cv, width, height);
91    else
92        ret = _cucul_set_canvas_size(cv, 80, 32);
93
94    if(ret < 0)
95    {
96#if defined(HAVE_ERRNO_H)
97        int saved_errno = errno;
98#endif
99        free(cv->allattr);
100        free(cv->allchars);
101        free(cv);
102#if defined(HAVE_ERRNO_H)
103        errno = saved_errno;
104#endif
105        return NULL;
106    }
107
108    /* FIXME: this shouldn't happen here */
109    _cucul_init_dither();
110
111    return cv;
112
113nomem:
114#if defined(HAVE_ERRNO_H)
115    errno = ENOMEM;
116#endif
117    return NULL;
118}
119
120/** \brief Resize a canvas.
121 *
122 *  This function sets the canvas width and height, in character cells.
123 *
124 *  The contents of the canvas are preserved to the extent of the new
125 *  canvas size. Newly allocated character cells at the right and/or at
126 *  the bottom of the canvas are filled with spaces.
127 *
128 *  It is an error to try to resize the canvas if an output driver has
129 *  been attached to the canvas using caca_create_display(). You need to
130 *  remove the output driver using caca_free_display() before you can change
131 *  the  canvas size again. However, the caca output driver can cause a
132 *  canvas resize through user interaction. See the caca_event() documentation
133 *  for more about this.
134 *
135 *  If an error occurs, -1 is returned and \b errno is set accordingly:
136 *  - \c EBUSY The canvas is in use by a display driver and cannot be resized.
137 *  - \c ENOMEM Not enough memory for the requested canvas size. If this
138 *    happens, the canvas handle becomes invalid and should not be used.
139 *
140 *  \param cv A libcucul canvas
141 *  \param width The desired canvas width
142 *  \param height The desired canvas height
143 *  \return 0 in case of success, -1 if an error occurred.
144 */
145int cucul_set_canvas_size(cucul_canvas_t *cv, unsigned int width,
146                                              unsigned int height)
147{
148    if(cv->refcount)
149    {
150#if defined(HAVE_ERRNO_H)
151        errno = EBUSY;
152#endif
153        return -1;
154    }
155
156    return _cucul_set_canvas_size(cv, width, height);
157}
158
159/** \brief Get the canvas width.
160 *
161 *  This function returns the current canvas width, in character cells.
162 *
163 *  This function never fails.
164 *
165 *  \param cv A libcucul canvas
166 *  \return The canvas width.
167 */
168unsigned int cucul_get_canvas_width(cucul_canvas_t *cv)
169{
170    return cv->width;
171}
172
173/** \brief Get the canvas height.
174 *
175 *  This function returns the current canvas height, in character cells.
176 *
177 *  This function never fails.
178 *
179 *  \param cv A libcucul canvas
180 *  \return The canvas height.
181 */
182unsigned int cucul_get_canvas_height(cucul_canvas_t *cv)
183{
184    return cv->height;
185}
186
187/** \brief Translate a colour index into the colour's name.
188 *
189 *  This function translates a cucul_color enum into a human-readable
190 *  description string of the associated colour.
191 *
192 *  This function never fails.
193 *
194 *  \param color The colour value.
195 *  \return A static string containing the colour's name, or \c "unknown" if
196 *  the colour is unknown.
197 */
198char const *cucul_get_color_name(unsigned int color)
199{
200    static char const *color_names[] =
201    {
202        "black",
203        "blue",
204        "green",
205        "cyan",
206        "red",
207        "magenta",
208        "brown",
209        "light gray",
210        "dark gray",
211        "light blue",
212        "light green",
213        "light cyan",
214        "light red",
215        "light magenta",
216        "yellow",
217        "white",
218    };
219
220    if(color < 0 || color > 15)
221        return "unknown";
222
223    return color_names[color];
224}
225
226/** \brief Uninitialise \e libcucul.
227 *
228 *  This function frees all resources allocated by cucul_create_canvas(). After
229 *  cucul_free_canvas() has been called, no other \e libcucul functions may be
230 *  used unless a new call to cucul_create_canvas() is done.
231 *
232 *  If an error occurs, -1 is returned and \b errno is set accordingly:
233 *  - \c EBUSY The canvas is in use by a display driver and cannot be freed.
234 *
235 *  \param cv A libcucul canvas
236 *  \return 0 in case of success, -1 if an error occurred.
237 */
238int cucul_free_canvas(cucul_canvas_t *cv)
239{
240    unsigned int f;
241
242    if(cv->refcount)
243    {
244#if defined(HAVE_ERRNO_H)
245        errno = EBUSY;
246#endif
247        return -1;
248    }
249
250    /* FIXME: this shouldn't be here either (see above) */
251    _cucul_end_dither();
252
253    for(f = 0; f < cv->framecount; f++)
254    {
255        free(cv->allchars[f]);
256        free(cv->allattr[f]);
257    }
258
259    free(cv);
260
261    return 0;
262}
263
264/** \brief Generate a random integer within a range.
265 *
266 *  This function never fails.
267 *
268 *  \param min The lower bound of the integer range.
269 *  \param max The upper bound of the integer range.
270 *  \return A random integer comprised between \p min  and \p max - 1
271 *  (inclusive).
272 */
273int cucul_rand(int min, int max)
274{
275    return min + (int)((1.0 * (max - min)) * rand() / (RAND_MAX + 1.0));
276}
277
278/*
279 * XXX: The following functions are local.
280 */
281
282int _cucul_set_canvas_size(cucul_canvas_t *cv, unsigned int width,
283                                               unsigned int height)
284{
285    unsigned int x, y, f, old_width, old_height, new_size, old_size;
286
287    old_width = cv->width;
288    old_height = cv->height;
289    old_size = old_width * old_height;
290
291    cv->width = width;
292    cv->height = height;
293    new_size = width * height;
294
295    /* Step 1: if new area is bigger, resize the memory area now. */
296    if(new_size > old_size)
297    {
298        for(f = 0; f < cv->framecount; f++)
299        {
300            cv->allchars[f] = realloc(cv->allchars[f],
301                                      new_size * sizeof(uint32_t));
302            cv->allattr[f] = realloc(cv->allattr[f],
303                                     new_size * sizeof(uint32_t));
304            if(!cv->allchars[f] || !cv->allattr[f])
305            {
306#if defined(HAVE_ERRNO_H)
307                errno = ENOMEM;
308#endif
309                return -1;
310            }
311        }
312    }
313
314    /* Step 2: move line data if necessary. */
315    if(width == old_width)
316    {
317        /* Width did not change, which means we do not need to move data. */
318        ;
319    }
320    else if(width > old_width)
321    {
322        /* New width is bigger than old width, which means we need to
323         * copy lines starting from the bottom of the screen otherwise
324         * we will overwrite information. */
325        for(f = 0; f < cv->framecount; f++)
326        {
327            uint32_t *chars = cv->allchars[f];
328            uint32_t *attr = cv->allattr[f];
329
330            for(y = height < old_height ? height : old_height; y--; )
331            {
332                for(x = old_width; x--; )
333                {
334                    chars[y * width + x] = chars[y * old_width + x];
335                    attr[y * width + x] = attr[y * old_width + x];
336                }
337
338                /* Zero the end of the line */
339                for(x = width - old_width; x--; )
340                    chars[y * width + old_width + x] = (uint32_t)' ';
341                memset(attr + y * width + old_width, 0,
342                       (width - old_width) * 4);
343            }
344        }
345    }
346    else
347    {
348        /* New width is smaller. Copy as many lines as possible. Ignore
349         * the first line, it is already in place. */
350        unsigned int lines = height < old_height ? height : old_height;
351
352        for(f = 0; f < cv->framecount; f++)
353        {
354            uint32_t *chars = cv->allchars[f];
355            uint32_t *attr = cv->allattr[f];
356
357            for(y = 1; y < lines; y++)
358            {
359                for(x = 0; x < width; x++)
360                {
361                    chars[y * width + x] = chars[y * old_width + x];
362                    attr[y * width + x] = attr[y * old_width + x];
363                }
364            }
365        }
366    }
367
368    /* Step 3: fill the bottom of the new screen if necessary. */
369    if(height > old_height)
370    {
371        for(f = 0; f < cv->framecount; f++)
372        {
373            uint32_t *chars = cv->allchars[f];
374            uint32_t *attr = cv->allattr[f];
375
376            /* Zero the bottom of the screen */
377            for(x = (height - old_height) * width; x--; )
378                chars[old_height * width + x] = (uint32_t)' ';
379            memset(attr + old_height * width, 0,
380                   (height - old_height) * width * 4);
381        }
382    }
383
384    /* Step 4: if new area is smaller, resize memory area now. */
385    if(new_size <= old_size)
386    {
387        for(f = 0; f < cv->framecount; f++)
388        {
389            cv->allchars[f] = realloc(cv->allchars[f],
390                                      new_size * sizeof(uint32_t));
391            cv->allattr[f] = realloc(cv->allattr[f],
392                                     new_size * sizeof(uint32_t));
393            if(!cv->allchars[f] || !cv->allattr[f])
394            {
395#if defined(HAVE_ERRNO_H)
396                errno = ENOMEM;
397#endif
398                return -1;
399            }
400        }
401    }
402
403    /* Reset the current frame shortcut */
404    cv->chars = cv->allchars[cv->frame];
405    cv->attr = cv->allattr[cv->frame];
406
407    return 0;
408}
409
Note: See TracBrowser for help on using the repository browser.