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

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