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

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