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

Last change on this file since 1231 was 1231, checked in by Sam Hocevar, 14 years ago
  • Removed "This function..." constructs from documentation. Fixed a few documentation errors or imprecisions.
  • 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 1231 2006-10-25 22:06:21Z 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 *  Initialise internal \e libcucul structures and the backend that will
42 *  be used for subsequent graphical operations. It must be the first
43 *  \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 *  Set 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 *  Return 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 *  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 *  Translate a cucul_color enum into a human-readable string describing
195 *  the corresponding 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 *  Free all resources allocated by cucul_create_canvas(). After
234 *  this function 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 *  Generate a random integer within the given range.
274 *
275 *  This function never fails.
276 *
277 *  \param min The lower bound of the integer range.
278 *  \param max The upper bound of the integer range.
279 *  \return A random integer comprised between \p min  and \p max - 1
280 *  (inclusive).
281 */
282int cucul_rand(int min, int max)
283{
284    static int need_init = 1;
285
286    if(need_init)
287    {
288        srand(getpid() + time(NULL));
289        need_init = 0;
290    }
291
292    return min + (int)((1.0 * (max - min)) * rand() / (RAND_MAX + 1.0));
293}
294
295/*
296 * XXX: The following functions are local.
297 */
298
299int _cucul_set_canvas_size(cucul_canvas_t *cv, unsigned int width,
300                                               unsigned int height)
301{
302    unsigned int x, y, f, old_width, old_height, new_size, old_size;
303
304    old_width = cv->width;
305    old_height = cv->height;
306    old_size = old_width * old_height;
307
308    cv->width = width;
309    cv->height = height;
310    new_size = width * height;
311
312    /* Step 1: if new area is bigger, resize the memory area now. */
313    if(new_size > old_size)
314    {
315        for(f = 0; f < cv->framecount; f++)
316        {
317            cv->allchars[f] = realloc(cv->allchars[f],
318                                      new_size * sizeof(uint32_t));
319            cv->allattr[f] = realloc(cv->allattr[f],
320                                     new_size * sizeof(uint32_t));
321            if(!cv->allchars[f] || !cv->allattr[f])
322            {
323#if defined(HAVE_ERRNO_H)
324                errno = ENOMEM;
325#endif
326                return -1;
327            }
328        }
329    }
330
331    /* Step 2: move line data if necessary. */
332    if(width == old_width)
333    {
334        /* Width did not change, which means we do not need to move data. */
335        ;
336    }
337    else if(width > old_width)
338    {
339        /* New width is bigger than old width, which means we need to
340         * copy lines starting from the bottom of the screen otherwise
341         * we will overwrite information. */
342        for(f = 0; f < cv->framecount; f++)
343        {
344            uint32_t *chars = cv->allchars[f];
345            uint32_t *attr = cv->allattr[f];
346
347            for(y = height < old_height ? height : old_height; y--; )
348            {
349                uint32_t color = (cv->bgcolor << 16) | cv->fgcolor;
350
351                for(x = old_width; x--; )
352                {
353                    chars[y * width + x] = chars[y * old_width + x];
354                    attr[y * width + x] = attr[y * old_width + x];
355                }
356
357                /* Zero the end of the line */
358                for(x = width - old_width; x--; )
359                {
360                    chars[y * width + old_width + x] = (uint32_t)' ';
361                    attr[y * width + old_width + x] = color;
362                }
363            }
364        }
365    }
366    else
367    {
368        /* New width is smaller. Copy as many lines as possible. Ignore
369         * the first line, it is already in place. */
370        unsigned int lines = height < old_height ? height : old_height;
371
372        for(f = 0; f < cv->framecount; f++)
373        {
374            uint32_t *chars = cv->allchars[f];
375            uint32_t *attr = cv->allattr[f];
376
377            for(y = 1; y < lines; y++)
378            {
379                for(x = 0; x < width; x++)
380                {
381                    chars[y * width + x] = chars[y * old_width + x];
382                    attr[y * width + x] = attr[y * old_width + x];
383                }
384            }
385        }
386    }
387
388    /* Step 3: fill the bottom of the new screen if necessary. */
389    if(height > old_height)
390    {
391        for(f = 0; f < cv->framecount; f++)
392        {
393            uint32_t *chars = cv->allchars[f];
394            uint32_t *attr = cv->allattr[f];
395            uint32_t color = (cv->bgcolor << 16) | cv->fgcolor;
396
397            /* Zero the bottom of the screen */
398            for(x = (height - old_height) * width; x--; )
399            {
400                chars[old_height * width + x] = (uint32_t)' ';
401                attr[old_height * width + x] = color;
402            }
403        }
404    }
405
406    /* Step 4: if new area is smaller, resize memory area now. */
407    if(new_size <= old_size)
408    {
409        for(f = 0; f < cv->framecount; f++)
410        {
411            cv->allchars[f] = realloc(cv->allchars[f],
412                                      new_size * sizeof(uint32_t));
413            cv->allattr[f] = realloc(cv->allattr[f],
414                                     new_size * sizeof(uint32_t));
415            if(!cv->allchars[f] || !cv->allattr[f])
416            {
417#if defined(HAVE_ERRNO_H)
418                errno = ENOMEM;
419#endif
420                return -1;
421            }
422        }
423    }
424
425    /* Reset the current frame shortcut */
426    cv->chars = cv->allchars[cv->frame];
427    cv->attr = cv->allattr[cv->frame];
428
429    return 0;
430}
431
Note: See TracBrowser for help on using the repository browser.