source: libcaca/trunk/caca/canvas.c @ 2821

Last change on this file since 2821 was 2821, checked in by Sam Hocevar, 11 years ago

Starting refactoring to get rid of libcucul. The initial reason for the
split is rendered moot by the plugin system: when enabled, binaries do
not link directly with libX11 or libGL. I hope this is a step towards
more consisteny and clarity.

  • Property svn:keywords set to Id
File size: 13.9 KB
Line 
1/*
2 *  libcaca       Colour ASCII-Art library
3 *  Copyright (c) 2002-2006 Sam Hocevar <sam@zoy.org>
4 *                All Rights Reserved
5 *
6 *  $Id: canvas.c 2821 2008-09-27 13:12:46Z sam $
7 *
8 *  This library is free software. It comes without any warranty, to
9 *  the extent permitted by applicable law. You can redistribute it
10 *  and/or modify it under the terms of the Do What The Fuck You Want
11 *  To Public License, Version 2, as published by Sam Hocevar. See
12 *  http://sam.zoy.org/wtfpl/COPYING for more details.
13 */
14
15/*
16 *  This file contains the main functions used by \e libcaca applications
17 *  to initialise a drawing context.
18 */
19
20#include "config.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 "caca.h"
34#include "caca_internals.h"
35
36static int caca_resize(caca_canvas_t *, int, int);
37
38/** \brief Initialise a \e libcaca canvas.
39 *
40 *  Initialise internal \e libcaca structures and the backend that will
41 *  be used for subsequent graphical operations. It must be the first
42 *  \e libcaca function to be called in a function. caca_free_canvas()
43 *  should be called at the end of the program to free all allocated resources.
44 *
45 *  Both the cursor and the canvas' handle are initialised at the top-left
46 *  corner.
47 *
48 *  If an error occurs, NULL is returned and \b errno is set accordingly:
49 *  - \c EINVAL Specified width or height is invalid.
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 libcaca canvas handle upon success, NULL if an error occurred.
55 */
56caca_canvas_t * caca_create_canvas(int width, int height)
57{
58    caca_canvas_t *cv;
59
60    if(width < 0 || height < 0)
61    {
62        seterrno(EINVAL);
63        return NULL;
64    }
65
66    cv = malloc(sizeof(caca_canvas_t));
67
68    if(!cv)
69        goto nomem;
70
71    cv->refcount = 0;
72    cv->autoinc = 0;
73    cv->resize_callback = NULL;
74    cv->resize_data = NULL;
75
76    cv->frame = 0;
77    cv->framecount = 1;
78    cv->frames = malloc(sizeof(struct caca_frame));
79    if(!cv->frames)
80    {
81        free(cv);
82        goto nomem;
83    }
84
85    cv->frames[0].width = cv->frames[0].height = 0;
86    cv->frames[0].chars = NULL;
87    cv->frames[0].attrs = NULL;
88    cv->frames[0].x = cv->frames[0].y = 0;
89    cv->frames[0].handlex = cv->frames[0].handley = 0;
90    cv->frames[0].curattr = 0;
91    cv->frames[0].name = strdup("frame#00000000");
92
93    _caca_load_frame_info(cv);
94    caca_set_color_ansi(cv, CACA_DEFAULT, CACA_TRANSPARENT);
95
96    cv->ff = NULL;
97
98    if(caca_resize(cv, width, height) < 0)
99    {
100        int saved_errno = geterrno();
101        free(cv->frames[0].name);
102        free(cv->frames);
103        free(cv);
104        seterrno(saved_errno);
105        return NULL;
106    }
107
108    return cv;
109
110nomem:
111    seterrno(ENOMEM);
112    return NULL;
113}
114
115/** \brief Manage a canvas.
116 *
117 *  Lock a canvas to prevent it from being resized. If non-NULL,
118 *  the \e callback function pointer will be called upon each
119 *  \e caca_set_canvas_size call and if the returned value is zero, the
120 *  canvas resize request will be denied.
121 *
122 *  This function is only useful for display drivers such as the \e libcaca
123 *  library.
124 *
125 *  If an error occurs, -1 is returned and \b errno is set accordingly:
126 *  - \c EBUSY The canvas is already being managed.
127 *
128 *  \param cv A libcaca canvas.
129 *  \param callback An optional callback function pointer.
130 *  \param p The argument to be passed to \e callback.
131 *  \return 0 in case of success, -1 if an error occurred.
132 */
133int caca_manage_canvas(caca_canvas_t *cv, int (*callback)(void *), void *p)
134{
135    if(cv->refcount)
136    {
137        seterrno(EBUSY);
138        return -1;
139    }
140
141    cv->resize_callback = callback;
142    cv->resize_data = p;
143    cv->refcount = 1;
144
145    return 0;
146}
147
148/** \brief Unmanage a canvas.
149 *
150 *  Unlock a canvas previously locked by caca_manage_canvas(). For safety
151 *  reasons, the callback and callback data arguments must be the same as for
152 *  the caca_manage_canvas() call.
153 *
154 *  This function is only useful for display drivers such as the \e libcaca
155 *  library.
156 *
157 *  If an error occurs, -1 is returned and \b errno is set accordingly:
158 *  - \c EINVAL The canvas is not managed, or the callback arguments do
159 *              not match.
160 *
161 *  \param cv A libcaca canvas.
162 *  \param callback The \e callback argument previously passed to
163                    caca_manage_canvas().
164 *  \param p The \e p argument previously passed to caca_manage_canvas().
165 *  \return 0 in case of success, -1 if an error occurred.
166 */
167int caca_unmanage_canvas(caca_canvas_t *cv, int (*callback)(void *), void *p)
168{
169    if(!cv->refcount
170        || cv->resize_callback != callback || cv->resize_data != p)
171    {
172        seterrno(EINVAL);
173        return -1;
174    }
175
176    cv->refcount = 0;
177
178    return 0;
179}
180
181/** \brief Resize a canvas.
182 *
183 *  Set the canvas' width and height, in character cells.
184 *
185 *  The contents of the canvas are preserved to the extent of the new
186 *  canvas size. Newly allocated character cells at the right and/or at
187 *  the bottom of the canvas are filled with spaces.
188 *
189 *  If as a result of the resize the cursor coordinates fall outside the
190 *  new canvas boundaries, they are readjusted. For instance, if the
191 *  current X cursor coordinate is 11 and the requested width is 10, the
192 *  new X cursor coordinate will be 10.
193 *
194 *  It is an error to try to resize the canvas if an output driver has
195 *  been attached to the canvas using caca_create_display(). You need to
196 *  remove the output driver using caca_free_display() before you can change
197 *  the canvas size again. However, the caca output driver can cause a
198 *  canvas resize through user interaction. See the caca_event() documentation
199 *  for more about this.
200 *
201 *  If an error occurs, -1 is returned and \b errno is set accordingly:
202 *  - \c EINVAL Specified width or height is invalid.
203 *  - \c EBUSY The canvas is in use by a display driver and cannot be resized.
204 *  - \c ENOMEM Not enough memory for the requested canvas size. If this
205 *    happens, the canvas handle becomes invalid and should not be used.
206 *
207 *  \param cv A libcaca canvas.
208 *  \param width The desired canvas width.
209 *  \param height The desired canvas height.
210 *  \return 0 in case of success, -1 if an error occurred.
211 */
212int caca_set_canvas_size(caca_canvas_t *cv, int width, int height)
213{
214    if(width < 0 || height < 0)
215    {
216        seterrno(EINVAL);
217        return -1;
218    }
219
220    if(cv->refcount && cv->resize_callback
221        && !cv->resize_callback(cv->resize_data))
222    {
223        seterrno(EBUSY);
224        return -1;
225    }
226
227    return caca_resize(cv, width, height);
228}
229
230/** \brief Get the canvas width.
231 *
232 *  Return the current canvas' width, in character cells.
233 *
234 *  This function never fails.
235 *
236 *  \param cv A libcaca canvas.
237 *  \return The canvas width.
238 */
239int caca_get_canvas_width(caca_canvas_t const *cv)
240{
241    return cv->width;
242}
243
244/** \brief Get the canvas height.
245 *
246 *  Returns the current canvas' height, in character cells.
247 *
248 *  This function never fails.
249 *
250 *  \param cv A libcaca canvas.
251 *  \return The canvas height.
252 */
253int caca_get_canvas_height(caca_canvas_t const *cv)
254{
255    return cv->height;
256}
257
258/** \brief Get the canvas character array.
259 *
260 *  Return the current canvas' internal character array. The array elements
261 *  consist in native endian 32-bit Unicode values as returned by
262 *  caca_get_char().
263 *
264 *  This function is only useful for display drivers such as the \e libcaca
265 *  library.
266 *
267 *  This function never fails.
268 *
269 *  \param cv A libcaca canvas.
270 *  \return The canvas character array.
271 */
272uint8_t const * caca_get_canvas_chars(caca_canvas_t const *cv)
273{
274    return (uint8_t const *)cv->chars;
275}
276
277/** \brief Get the canvas attribute array.
278 *
279 *  Returns the current canvas' internal attribute array. The array elements
280 *  consist in native endian 32-bit attribute values as returned by
281 *  caca_get_attr().
282 *
283 *  This function is only useful for display drivers such as the \e libcaca
284 *  library.
285 *
286 *  This function never fails.
287 *
288 *  \param cv A libcaca canvas.
289 *  \return The canvas attribute array.
290 */
291uint8_t const * caca_get_canvas_attrs(caca_canvas_t const *cv)
292{
293    return (uint8_t const *)cv->attrs;
294}
295
296/** \brief Uninitialise \e libcaca.
297 *
298 *  Free all resources allocated by caca_create_canvas(). After
299 *  this function has been called, no other \e libcaca functions may be
300 *  used unless a new call to caca_create_canvas() is done.
301 *
302 *  If an error occurs, -1 is returned and \b errno is set accordingly:
303 *  - \c EBUSY The canvas is in use by a display driver and cannot be freed.
304 *
305 *  \param cv A libcaca canvas.
306 *  \return 0 in case of success, -1 if an error occurred.
307 */
308int caca_free_canvas(caca_canvas_t *cv)
309{
310    int f;
311
312    if(cv->refcount)
313    {
314        seterrno(EBUSY);
315        return -1;
316    }
317
318    for(f = 0; f < cv->framecount; f++)
319    {
320        free(cv->frames[f].chars);
321        free(cv->frames[f].attrs);
322        free(cv->frames[f].name);
323    }
324
325    caca_canvas_set_figfont(cv, NULL);
326
327    free(cv->frames);
328    free(cv);
329
330    return 0;
331}
332
333/** \brief Generate a random integer within a range.
334 *
335 *  Generate a random integer within the given range.
336 *
337 *  This function never fails.
338 *
339 *  \param min The lower bound of the integer range.
340 *  \param max The upper bound of the integer range.
341 *  \return A random integer comprised between \p min  and \p max - 1
342 *  (inclusive).
343 */
344int caca_rand(int min, int max)
345{
346    static int need_init = 1;
347
348    if(need_init)
349    {
350        srand(getpid() + time(NULL));
351        need_init = 0;
352    }
353
354    return min + (int)((1.0 * (max - min)) * rand() / (RAND_MAX + 1.0));
355}
356
357
358/*
359 * XXX: The following functions are local.
360 */
361
362int caca_resize(caca_canvas_t *cv, int width, int height)
363{
364    int x, y, f, old_width, old_height, new_size, old_size;
365
366    old_width = cv->width;
367    old_height = cv->height;
368    old_size = old_width * old_height;
369
370    _caca_save_frame_info(cv);
371
372    cv->width = width;
373    cv->height = height;
374    new_size = width * height;
375
376    /* Step 1: if new area is bigger, resize the memory area now. */
377    if(new_size > old_size)
378    {
379        for(f = 0; f < cv->framecount; f++)
380        {
381            cv->frames[f].chars = realloc(cv->frames[f].chars,
382                                          new_size * sizeof(uint32_t));
383            cv->frames[f].attrs = realloc(cv->frames[f].attrs,
384                                          new_size * sizeof(uint32_t));
385            if(new_size && (!cv->frames[f].chars || !cv->frames[f].attrs))
386            {
387                seterrno(ENOMEM);
388                return -1;
389            }
390        }
391    }
392
393    /* Step 2: move line data if necessary. */
394    if(width == old_width)
395    {
396        /* Width did not change, which means we do not need to move data. */
397        ;
398    }
399    else if(width > old_width)
400    {
401        /* New width is bigger than old width, which means we need to
402         * copy lines starting from the bottom of the screen otherwise
403         * we will overwrite information. */
404        for(f = 0; f < cv->framecount; f++)
405        {
406            uint32_t *chars = cv->frames[f].chars;
407            uint32_t *attrs = cv->frames[f].attrs;
408
409            for(y = height < old_height ? height : old_height; y--; )
410            {
411                uint32_t attr = cv->frames[f].curattr;
412
413                for(x = old_width; x--; )
414                {
415                    chars[y * width + x] = chars[y * old_width + x];
416                    attrs[y * width + x] = attrs[y * old_width + x];
417                }
418
419                /* Zero the end of the line */
420                for(x = width - old_width; x--; )
421                {
422                    chars[y * width + old_width + x] = (uint32_t)' ';
423                    attrs[y * width + old_width + x] = attr;
424                }
425            }
426        }
427    }
428    else
429    {
430        /* New width is smaller. Copy as many lines as possible. Ignore
431         * the first line, it is already in place. */
432        int lines = height < old_height ? height : old_height;
433
434        for(f = 0; f < cv->framecount; f++)
435        {
436            uint32_t *chars = cv->frames[f].chars;
437            uint32_t *attrs = cv->frames[f].attrs;
438
439            for(y = 1; y < lines; y++)
440            {
441                for(x = 0; x < width; x++)
442                {
443                    chars[y * width + x] = chars[y * old_width + x];
444                    attrs[y * width + x] = attrs[y * old_width + x];
445                }
446            }
447        }
448    }
449
450    /* Step 3: fill the bottom of the new screen if necessary. */
451    if(height > old_height)
452    {
453        for(f = 0; f < cv->framecount; f++)
454        {
455            uint32_t *chars = cv->frames[f].chars;
456            uint32_t *attrs = cv->frames[f].attrs;
457            uint32_t attr = cv->frames[f].curattr;
458
459            /* Zero the bottom of the screen */
460            for(x = (height - old_height) * width; x--; )
461            {
462                chars[old_height * width + x] = (uint32_t)' ';
463                attrs[old_height * width + x] = attr;
464            }
465        }
466    }
467
468    /* Step 4: if new area is smaller, resize memory area now. */
469    if(new_size < old_size)
470    {
471        for(f = 0; f < cv->framecount; f++)
472        {
473            cv->frames[f].chars = realloc(cv->frames[f].chars,
474                                          new_size * sizeof(uint32_t));
475            cv->frames[f].attrs = realloc(cv->frames[f].attrs,
476                                          new_size * sizeof(uint32_t));
477            if(new_size && (!cv->frames[f].chars || !cv->frames[f].attrs))
478            {
479                seterrno(ENOMEM);
480                return -1;
481            }
482        }
483    }
484
485    /* Set new size */
486    for(f = 0; f < cv->framecount; f++)
487    {
488        if(cv->frames[f].x > (int)width)
489            cv->frames[f].x = width;
490        if(cv->frames[f].y > (int)height)
491            cv->frames[f].y = height;
492
493        cv->frames[f].width = width;
494        cv->frames[f].height = height;
495    }
496
497    /* Reset the current frame shortcuts */
498    _caca_load_frame_info(cv);
499
500    return 0;
501}
502
Note: See TracBrowser for help on using the repository browser.