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

Last change on this file since 1381 was 1381, checked in by Sam Hocevar, 16 years ago
  • Fix a bug in cucul_set_canvas_size() that caused the default attribute to be ignored.
  • Property svn:keywords set to Id
File size: 10.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 1381 2006-11-12 22:26:36Z 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#   include <sys/types.h>
28#   if defined(HAVE_UNISTD_H)
29#       include <unistd.h>
30#   endif
31#endif
32
33#include "cucul.h"
34#include "cucul_internals.h"
35
36/** \brief Initialise a \e libcucul canvas.
37 *
38 *  Initialise internal \e libcucul structures and the backend that will
39 *  be used for subsequent graphical operations. It must be the first
40 *  \e libcucul function to be called in a function. cucul_free_canvas()
41 *  should be called at the end of the program to free all allocated resources.
42 *
43 *  Both the cursor and the canvas' handle are initialised at the top-left
44 *  corner.
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->frame = 0;
63    cv->framecount = 1;
64    cv->frames = malloc(sizeof(struct cucul_frame));
65    if(!cv->frames)
66    {
67        free(cv);
68        goto nomem;
69    }
70
71    cv->frames[0].width = cv->frames[0].height = 0;
72    cv->frames[0].chars = NULL;
73    cv->frames[0].attrs = NULL;
74    cv->frames[0].x = cv->frames[0].y = 0;
75    cv->frames[0].handlex = cv->frames[0].handley = 0;
76    cv->frames[0].curattr = 0;
77
78    _cucul_load_frame_info(cv);
79    cucul_set_color_ansi(cv, CUCUL_DEFAULT, CUCUL_TRANSPARENT);
80
81    if(_cucul_set_canvas_size(cv, width, height) < 0)
82    {
83        int saved_errno = geterrno();
84        free(cv->frames);
85        free(cv);
86        seterrno(saved_errno);
87        return NULL;
88    }
89
90    return cv;
91
92nomem:
93    seterrno(ENOMEM);
94    return NULL;
95}
96
97/** \brief Resize a canvas.
98 *
99 *  Set the canvas' width and height, in character cells.
100 *
101 *  The contents of the canvas are preserved to the extent of the new
102 *  canvas size. Newly allocated character cells at the right and/or at
103 *  the bottom of the canvas are filled with spaces.
104 *
105 *  It is an error to try to resize the canvas if an output driver has
106 *  been attached to the canvas using caca_create_display(). You need to
107 *  remove the output driver using caca_free_display() before you can change
108 *  the  canvas size again. However, the caca output driver can cause a
109 *  canvas resize through user interaction. See the caca_event() documentation
110 *  for more about this.
111 *
112 *  If an error occurs, -1 is returned and \b errno is set accordingly:
113 *  - \c EBUSY The canvas is in use by a display driver and cannot be resized.
114 *  - \c ENOMEM Not enough memory for the requested canvas size. If this
115 *    happens, the canvas handle becomes invalid and should not be used.
116 *
117 *  \param cv A libcucul canvas
118 *  \param width The desired canvas width
119 *  \param height The desired canvas height
120 *  \return 0 in case of success, -1 if an error occurred.
121 */
122int cucul_set_canvas_size(cucul_canvas_t *cv, unsigned int width,
123                                              unsigned int height)
124{
125    if(cv->refcount)
126    {
127        seterrno(EBUSY);
128        return -1;
129    }
130
131    return _cucul_set_canvas_size(cv, width, height);
132}
133
134/** \brief Get the canvas width.
135 *
136 *  Return the current canvas' width, in character cells.
137 *
138 *  This function never fails.
139 *
140 *  \param cv A libcucul canvas
141 *  \return The canvas width.
142 */
143unsigned int cucul_get_canvas_width(cucul_canvas_t *cv)
144{
145    return cv->width;
146}
147
148/** \brief Get the canvas height.
149 *
150 *  Returns the current canvas' height, in character cells.
151 *
152 *  This function never fails.
153 *
154 *  \param cv A libcucul canvas
155 *  \return The canvas height.
156 */
157unsigned int cucul_get_canvas_height(cucul_canvas_t *cv)
158{
159    return cv->height;
160}
161
162/** \brief Uninitialise \e libcucul.
163 *
164 *  Free all resources allocated by cucul_create_canvas(). After
165 *  this function has been called, no other \e libcucul functions may be
166 *  used unless a new call to cucul_create_canvas() is done.
167 *
168 *  If an error occurs, -1 is returned and \b errno is set accordingly:
169 *  - \c EBUSY The canvas is in use by a display driver and cannot be freed.
170 *
171 *  \param cv A libcucul canvas
172 *  \return 0 in case of success, -1 if an error occurred.
173 */
174int cucul_free_canvas(cucul_canvas_t *cv)
175{
176    unsigned int f;
177
178    if(cv->refcount)
179    {
180        seterrno(EBUSY);
181        return -1;
182    }
183
184    for(f = 0; f < cv->framecount; f++)
185    {
186        free(cv->frames[f].chars);
187        free(cv->frames[f].attrs);
188    }
189
190    free(cv->frames);
191    free(cv);
192
193    return 0;
194}
195
196/** \brief Generate a random integer within a range.
197 *
198 *  Generate a random integer within the given range.
199 *
200 *  This function never fails.
201 *
202 *  \param min The lower bound of the integer range.
203 *  \param max The upper bound of the integer range.
204 *  \return A random integer comprised between \p min  and \p max - 1
205 *  (inclusive).
206 */
207int cucul_rand(int min, int max)
208{
209    static int need_init = 1;
210
211    if(need_init)
212    {
213        srand(getpid() + time(NULL));
214        need_init = 0;
215    }
216
217    return min + (int)((1.0 * (max - min)) * rand() / (RAND_MAX + 1.0));
218}
219
220/*
221 * XXX: The following functions are local.
222 */
223
224int _cucul_set_canvas_size(cucul_canvas_t *cv, unsigned int width,
225                                               unsigned int height)
226{
227    unsigned int x, y, f, old_width, old_height, new_size, old_size;
228
229    old_width = cv->width;
230    old_height = cv->height;
231    old_size = old_width * old_height;
232
233    _cucul_save_frame_info(cv);
234
235    cv->width = width;
236    cv->height = height;
237    new_size = width * height;
238
239    /* Step 1: if new area is bigger, resize the memory area now. */
240    if(new_size > old_size)
241    {
242        for(f = 0; f < cv->framecount; f++)
243        {
244            cv->frames[f].chars = realloc(cv->frames[f].chars,
245                                          new_size * sizeof(uint32_t));
246            cv->frames[f].attrs = realloc(cv->frames[f].attrs,
247                                          new_size * sizeof(uint32_t));
248            if(new_size && (!cv->frames[f].chars || !cv->frames[f].attrs))
249            {
250                seterrno(ENOMEM);
251                return -1;
252            }
253        }
254    }
255
256    /* Step 2: move line data if necessary. */
257    if(width == old_width)
258    {
259        /* Width did not change, which means we do not need to move data. */
260        ;
261    }
262    else if(width > old_width)
263    {
264        /* New width is bigger than old width, which means we need to
265         * copy lines starting from the bottom of the screen otherwise
266         * we will overwrite information. */
267        for(f = 0; f < cv->framecount; f++)
268        {
269            uint32_t *chars = cv->frames[f].chars;
270            uint32_t *attrs = cv->frames[f].attrs;
271
272            for(y = height < old_height ? height : old_height; y--; )
273            {
274                uint32_t attr = cv->frames[f].curattr;
275
276                for(x = old_width; x--; )
277                {
278                    chars[y * width + x] = chars[y * old_width + x];
279                    attrs[y * width + x] = attrs[y * old_width + x];
280                }
281
282                /* Zero the end of the line */
283                for(x = width - old_width; x--; )
284                {
285                    chars[y * width + old_width + x] = (uint32_t)' ';
286                    attrs[y * width + old_width + x] = attr;
287                }
288            }
289        }
290    }
291    else
292    {
293        /* New width is smaller. Copy as many lines as possible. Ignore
294         * the first line, it is already in place. */
295        unsigned int lines = height < old_height ? height : old_height;
296
297        for(f = 0; f < cv->framecount; f++)
298        {
299            uint32_t *chars = cv->frames[f].chars;
300            uint32_t *attrs = cv->frames[f].attrs;
301
302            for(y = 1; y < lines; y++)
303            {
304                for(x = 0; x < width; x++)
305                {
306                    chars[y * width + x] = chars[y * old_width + x];
307                    attrs[y * width + x] = attrs[y * old_width + x];
308                }
309            }
310        }
311    }
312
313    /* Step 3: fill the bottom of the new screen if necessary. */
314    if(height > old_height)
315    {
316        for(f = 0; f < cv->framecount; f++)
317        {
318            uint32_t *chars = cv->frames[f].chars;
319            uint32_t *attrs = cv->frames[f].attrs;
320            uint32_t attr = cv->frames[f].curattr;
321
322            /* Zero the bottom of the screen */
323            for(x = (height - old_height) * width; x--; )
324            {
325                chars[old_height * width + x] = (uint32_t)' ';
326                attrs[old_height * width + x] = attr;
327            }
328        }
329    }
330
331    /* Step 4: if new area is smaller, resize memory area now. */
332    if(new_size < old_size)
333    {
334        for(f = 0; f < cv->framecount; f++)
335        {
336            cv->frames[f].chars = realloc(cv->frames[f].chars,
337                                          new_size * sizeof(uint32_t));
338            cv->frames[f].attrs = realloc(cv->frames[f].attrs,
339                                          new_size * sizeof(uint32_t));
340            if(new_size && (!cv->frames[f].chars || !cv->frames[f].attrs))
341            {
342                seterrno(ENOMEM);
343                return -1;
344            }
345        }
346    }
347
348    /* Set new size */
349    for(f = 0; f < cv->framecount; f++)
350    {
351        cv->frames[f].width = width;
352        cv->frames[f].height = height;
353    }
354
355    /* Reset the current frame shortcuts */
356    _cucul_load_frame_info(cv);
357
358    return 0;
359}
360
Note: See TracBrowser for help on using the repository browser.