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

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