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

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