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
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 3470 2009-05-19 00:51:47Z 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->dirty_xmin = 0;
97    cv->dirty_xmax = -1;
98    cv->dirty_ymin = 0;
99    cv->dirty_ymax = -1;
100
101    cv->ff = NULL;
102
103    if(caca_resize(cv, width, height) < 0)
104    {
105        int saved_errno = geterrno();
106        free(cv->frames[0].name);
107        free(cv->frames);
108        free(cv);
109        seterrno(saved_errno);
110        return NULL;
111    }
112
113    return cv;
114
115nomem:
116    seterrno(ENOMEM);
117    return NULL;
118}
119
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
124 *  \e caca_set_canvas_size call and if the returned value is zero, the
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 *
133 *  \param cv A libcaca canvas.
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 */
138int caca_manage_canvas(caca_canvas_t *cv, int (*callback)(void *), void *p)
139{
140    if(cv->refcount)
141    {
142        seterrno(EBUSY);
143        return -1;
144    }
145
146    cv->resize_callback = callback;
147    cv->resize_data = p;
148    cv->refcount = 1;
149
150    return 0;
151}
152
153/** \brief unmanage a canvas.
154 *
155 *  unlock a canvas previously locked by caca_manage_canvas(). for safety
156 *  reasons, the callback and callback data arguments must be the same as for
157 *  the caca_manage_canvas() call.
158 *
159 *  this function is only useful for display drivers such as the \e libcaca
160 *  library.
161 *
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
164 *              not match.
165 *
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().
170 *  \return 0 in case of success, -1 if an error occurred.
171 */
172int caca_unmanage_canvas(caca_canvas_t *cv, int (*callback)(void *), void *p)
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
186/** \brief Resize a canvas.
187 *
188 *  Set the canvas' width and height, in character cells.
189 *
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 *
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 *
199 *  It is an error to try to resize the canvas if an output driver has
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
202 *  the canvas size again. However, the caca output driver can cause a
203 *  canvas resize through user interaction. See the caca_event() documentation
204 *  for more about this.
205 *
206 *  If an error occurs, -1 is returned and \b errno is set accordingly:
207 *  - \c EINVAL Specified width or height is invalid.
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 *
212 *  \param cv A libcaca canvas.
213 *  \param width The desired canvas width.
214 *  \param height The desired canvas height.
215 *  \return 0 in case of success, -1 if an error occurred.
216 */
217int caca_set_canvas_size(caca_canvas_t *cv, int width, int height)
218{
219    if(width < 0 || height < 0)
220    {
221        seterrno(EINVAL);
222        return -1;
223    }
224
225    if(cv->refcount && cv->resize_callback
226        && !cv->resize_callback(cv->resize_data))
227    {
228        seterrno(EBUSY);
229        return -1;
230    }
231
232    return caca_resize(cv, width, height);
233}
234
235/** \brief Get the canvas width.
236 *
237 *  Return the current canvas' width, in character cells.
238 *
239 *  This function never fails.
240 *
241 *  \param cv A libcaca canvas.
242 *  \return The canvas width.
243 */
244int caca_get_canvas_width(caca_canvas_t const *cv)
245{
246    return cv->width;
247}
248
249/** \brief Get the canvas height.
250 *
251 *  Returns the current canvas' height, in character cells.
252 *
253 *  This function never fails.
254 *
255 *  \param cv A libcaca canvas.
256 *  \return The canvas height.
257 */
258int caca_get_canvas_height(caca_canvas_t const *cv)
259{
260    return cv->height;
261}
262
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
267 *  caca_get_char().
268 *
269 *  This function is only useful for display drivers such as the \e libcaca
270 *  library.
271 *
272 *  This function never fails.
273 *
274 *  \param cv A libcaca canvas.
275 *  \return The canvas character array.
276 */
277uint8_t const * caca_get_canvas_chars(caca_canvas_t const *cv)
278{
279    return (uint8_t const *)cv->chars;
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
286 *  caca_get_attr().
287 *
288 *  This function is only useful for display drivers such as the \e libcaca
289 *  library.
290 *
291 *  This function never fails.
292 *
293 *  \param cv A libcaca canvas.
294 *  \return The canvas attribute array.
295 */
296uint8_t const * caca_get_canvas_attrs(caca_canvas_t const *cv)
297{
298    return (uint8_t const *)cv->attrs;
299}
300
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 *
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 *
310 *  \param cv A libcaca canvas.
311 *  \return 0 in case of success, -1 if an error occurred.
312 */
313int caca_free_canvas(caca_canvas_t *cv)
314{
315    int f;
316
317    if(cv->refcount)
318    {
319        seterrno(EBUSY);
320        return -1;
321    }
322
323    for(f = 0; f < cv->framecount; f++)
324    {
325        free(cv->frames[f].chars);
326        free(cv->frames[f].attrs);
327        free(cv->frames[f].name);
328    }
329
330    caca_canvas_set_figfont(cv, NULL);
331
332    free(cv->frames);
333    free(cv);
334
335    return 0;
336}
337
338/** \brief Generate a random integer within a range.
339 *
340 *  Generate a random integer within the given range.
341 *
342 *  This function never fails.
343 *
344 *  \param min The lower bound of the integer range.
345 *  \param max The upper bound of the integer range.
346 *  \return A random integer comprised between \p min  and \p max - 1
347 *  (inclusive).
348 */
349int caca_rand(int min, int max)
350{
351    static int need_init = 1;
352
353    if(need_init)
354    {
355        srand(getpid() + time(NULL));
356        need_init = 0;
357    }
358
359    return min + (int)((1.0 * (max - min)) * rand() / (RAND_MAX + 1.0));
360}
361
362
363/*
364 * XXX: The following functions are local.
365 */
366
367int caca_resize(caca_canvas_t *cv, int width, int height)
368{
369    int x, y, f, old_width, old_height, new_size, old_size;
370
371    old_width = cv->width;
372    old_height = cv->height;
373    old_size = old_width * old_height;
374
375    _caca_save_frame_info(cv);
376
377    cv->width = width;
378    cv->height = height;
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    {
384        for(f = 0; f < cv->framecount; f++)
385        {
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))
391            {
392                seterrno(ENOMEM);
393                return -1;
394            }
395        }
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. */
409        for(f = 0; f < cv->framecount; f++)
410        {
411            uint32_t *chars = cv->frames[f].chars;
412            uint32_t *attrs = cv->frames[f].attrs;
413
414            for(y = height < old_height ? height : old_height; y--; )
415            {
416                uint32_t attr = cv->frames[f].curattr;
417
418                for(x = old_width; x--; )
419                {
420                    chars[y * width + x] = chars[y * old_width + x];
421                    attrs[y * width + x] = attrs[y * old_width + x];
422                }
423
424                /* Zero the end of the line */
425                for(x = width - old_width; x--; )
426                {
427                    chars[y * width + old_width + x] = (uint32_t)' ';
428                    attrs[y * width + old_width + x] = attr;
429                }
430            }
431        }
432
433        caca_add_dirty_rectangle(cv, old_width, 0, width - 1, old_height - 1);
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. */
439        int lines = height < old_height ? height : old_height;
440
441        for(f = 0; f < cv->framecount; f++)
442        {
443            uint32_t *chars = cv->frames[f].chars;
444            uint32_t *attrs = cv->frames[f].attrs;
445
446            for(y = 1; y < lines; y++)
447            {
448                for(x = 0; x < width; x++)
449                {
450                    chars[y * width + x] = chars[y * old_width + x];
451                    attrs[y * width + x] = attrs[y * old_width + x];
452                }
453            }
454        }
455    }
456
457    /* Step 3: fill the bottom of the new screen if necessary. */
458    if(height > old_height)
459    {
460        for(f = 0; f < cv->framecount; f++)
461        {
462            uint32_t *chars = cv->frames[f].chars;
463            uint32_t *attrs = cv->frames[f].attrs;
464            uint32_t attr = cv->frames[f].curattr;
465
466            /* Zero the bottom of the screen */
467            for(x = (height - old_height) * width; x--; )
468            {
469                chars[old_height * width + x] = (uint32_t)' ';
470                attrs[old_height * width + x] = attr;
471            }
472        }
473
474        caca_add_dirty_rectangle(cv, 0, old_height, old_width - 1, height - 1);
475    }
476
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
484    /* Step 4: if new area is smaller, resize memory area now. */
485    if(new_size < old_size)
486    {
487        for(f = 0; f < cv->framecount; f++)
488        {
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))
494            {
495                seterrno(ENOMEM);
496                return -1;
497            }
498        }
499    }
500
501    /* Set new size */
502    for(f = 0; f < cv->framecount; f++)
503    {
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
509        cv->frames[f].width = width;
510        cv->frames[f].height = height;
511    }
512
513    /* Reset the current frame shortcuts */
514    _caca_load_frame_info(cv);
515
516    return 0;
517}
518
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.