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

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

Change the dirty rectangle API so that it can handle several rectangles. The
inner implementation still only handles one dirty rectangle, but this way
we can prepare supporting applictions for the future.

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