source: libcaca/trunk/caca/string.c @ 3586

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

Implement caca_vprintf() to allow third-party variadic functions to call us.

  • Property svn:keywords set to Id
File size: 17.3 KB
Line 
1/*
2 *  libcaca       Colour ASCII-Art library
3 *  Copyright (c) 2002-2009 Sam Hocevar <sam@hocevar.net>
4 *                All Rights Reserved
5 *
6 *  $Id: string.c 3586 2009-07-26 23:26:08Z 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 various canvas handling functions such as character
17 *  and string drawing.
18 */
19
20#include "config.h"
21
22#if !defined(__KERNEL__)
23#   include <stdio.h> /* BUFSIZ */
24#   include <string.h>
25#   include <stdlib.h>
26#   include <stdarg.h>
27#   if defined(HAVE_UNISTD_H)
28#       include <unistd.h>
29#   endif
30#   if defined(HAVE_SIGNAL_H)
31#       include <signal.h>
32#   endif
33#   if defined(HAVE_SYS_IOCTL_H)
34#       include <sys/ioctl.h>
35#   endif
36#endif
37
38#include "caca.h"
39#include "caca_internals.h"
40
41/** \brief Set cursor position.
42 *
43 *  Put the cursor at the given coordinates. Functions making use of the
44 *  cursor will use the new values. Setting the cursor position outside the
45 *  canvas is legal but the cursor will not be shown.
46 *
47 *  This function never fails.
48 *
49 *  \param cv A handle to the libcaca canvas.
50 *  \param x X cursor coordinate.
51 *  \param y Y cursor coordinate.
52 *  \return This function always returns 0.
53 */
54int caca_gotoxy(caca_canvas_t *cv, int x, int y)
55{
56    cv->frames[cv->frame].x = x;
57    cv->frames[cv->frame].y = y;
58
59    return 0;
60}
61
62/** \brief Get X cursor position.
63 *
64 *  Retrieve the X coordinate of the cursor's position.
65 *
66 *  This function never fails.
67 *
68 *  \param cv A handle to the libcaca canvas.
69 *  \return The cursor's X coordinate.
70 */
71int caca_get_cursor_x(caca_canvas_t const *cv)
72{
73    return cv->frames[cv->frame].x;
74}
75
76/** \brief Get Y cursor position.
77 *
78 *  Retrieve the Y coordinate of the cursor's position.
79 *
80 *  This function never fails.
81 *
82 *  \param cv A handle to the libcaca canvas.
83 *  \return The cursor's Y coordinate.
84 */
85int caca_get_cursor_y(caca_canvas_t const *cv)
86{
87    return cv->frames[cv->frame].y;
88}
89
90/** \brief Print an ASCII or Unicode character.
91 *
92 *  Print an ASCII or Unicode character at the given coordinates, using
93 *  the default foreground and background colour values.
94 *
95 *  If the coordinates are outside the canvas boundaries, nothing is printed.
96 *  If a fullwidth Unicode character gets overwritten, its remaining visible
97 *  parts are replaced with spaces. If the canvas' boundaries would split the
98 *  fullwidth character in two, a space is printed instead.
99 *
100 *  The behaviour when printing non-printable characters or invalid UTF-32
101 *  characters is undefined. To print a sequence of bytes forming an UTF-8
102 *  character instead of an UTF-32 character, use the caca_put_str() function.
103 *
104 *  This function never fails.
105 *
106 *  \param cv A handle to the libcaca canvas.
107 *  \param x X coordinate.
108 *  \param y Y coordinate.
109 *  \param ch The character to print.
110 *  \return This function always returns 0.
111 */
112int caca_put_char(caca_canvas_t *cv, int x, int y, uint32_t ch)
113{
114    uint32_t *curchar, *curattr, attr;
115    int fullwidth, xmin, xmax;
116
117    if(x >= (int)cv->width || y < 0 || y >= (int)cv->height)
118        return 0;
119
120    if(ch == CACA_MAGIC_FULLWIDTH)
121        return 0;
122
123    fullwidth = caca_utf32_is_fullwidth(ch);
124
125    if(x == -1 && fullwidth)
126    {
127        x = 0;
128        ch = ' ';
129        fullwidth = 0;
130    }
131    else if(x < 0)
132        return 0;
133
134    curchar = cv->chars + x + y * cv->width;
135    curattr = cv->attrs + x + y * cv->width;
136    attr = cv->curattr;
137
138    xmin = xmax = x;
139
140    /* When overwriting the right part of a fullwidth character,
141     * replace its left part with a space. */
142    if(x && curchar[0] == CACA_MAGIC_FULLWIDTH)
143    {
144        curchar[-1] = ' ';
145        xmin--;
146    }
147
148    if(fullwidth)
149    {
150        if(x + 1 == (int)cv->width)
151            ch = ' ';
152        else
153        {
154            xmax++;
155
156            /* When overwriting the left part of a fullwidth character,
157             * replace its right part with a space. */
158            if(x + 2 < (int)cv->width && curchar[2] == CACA_MAGIC_FULLWIDTH)
159            {
160                curchar[2] = ' ';
161                xmax++;
162            }
163
164            curchar[1] = CACA_MAGIC_FULLWIDTH;
165            curattr[1] = attr;
166        }
167    }
168    else
169    {
170        /* When overwriting the left part of a fullwidth character,
171         * replace its right part with a space. */
172        if(x + 1 != (int)cv->width && curchar[1] == CACA_MAGIC_FULLWIDTH)
173        {
174            curchar[1] = ' ';
175            xmax++;
176        }
177    }
178
179    /* Only add a dirty rectangle if we are pasting a different character
180     * or attribute at that place. This does not account for inconsistencies
181     * in the canvas, ie. if CACA_MAGIC_FULLWIDTH lies at illegal places,
182     * but it's the caller's responsibility not to corrupt the contents. */
183    if(!cv->dirty_disabled
184        && (curchar[0] != ch || curattr[0] != attr))
185        caca_add_dirty_rect(cv, xmin, y, xmax - xmin + 1, 1);
186
187    curchar[0] = ch;
188    curattr[0] = attr;
189
190    return 0;
191}
192
193/** \brief Get the Unicode character at the given coordinates.
194 *
195 *  Get the ASCII or Unicode value of the character at the given
196 *  coordinates. If the value is less or equal to 127 (0x7f),
197 *  the character can be printed as ASCII. Otherise, it must be handled
198 *  as a UTF-32 value.
199 *
200 *  If the coordinates are outside the canvas boundaries, a space (0x20)
201 *  is returned.
202 *
203 *  A special exception is when CACA_MAGIC_FULLWIDTH is returned. This
204 *  value is guaranteed not to be a valid Unicode character, and indicates
205 *  that the character at the left of the requested one is a fullwidth
206 *  character.
207 *
208 *  This function never fails.
209 *
210 *  \param cv A handle to the libcaca canvas.
211 *  \param x X coordinate.
212 *  \param y Y coordinate.
213 *  \return The Unicode character at the given coordinates.
214 */
215uint32_t caca_get_char(caca_canvas_t const *cv, int x, int y)
216{
217    if(x < 0 || x >= (int)cv->width || y < 0 || y >= (int)cv->height)
218        return ' ';
219
220    return cv->chars[x + y * cv->width];
221}
222
223/** \brief Print a string.
224 *
225 *  Print an UTF-8 string at the given coordinates, using the default
226 *  foreground and background values. The coordinates may be outside the
227 *  canvas boundaries (eg. a negative Y coordinate) and the string will
228 *  be cropped accordingly if it is too long.
229 *
230 *  See caca_put_char() for more information on how fullwidth characters
231 *  are handled when overwriting each other or at the canvas' boundaries.
232 *
233 *  This function never fails.
234 *
235 *  \param cv A handle to the libcaca canvas.
236 *  \param x X coordinate.
237 *  \param y Y coordinate.
238 *  \param s The string to print.
239 *  \return This function always returns 0.
240 */
241int caca_put_str(caca_canvas_t *cv, int x, int y, char const *s)
242{
243    size_t rd;
244
245    if(y < 0 || y >= (int)cv->height || x >= (int)cv->width)
246        return 0;
247
248    while(*s && x < -1)
249    {
250        x += caca_utf32_is_fullwidth(caca_utf8_to_utf32(s, &rd)) ? 2 : 1;
251        s += rd;
252    }
253
254    while(*s && x < (int)cv->width)
255    {
256        uint32_t ch = caca_utf8_to_utf32(s, &rd);
257        caca_put_char(cv, x, y, ch);
258        x += caca_utf32_is_fullwidth(ch) ? 2 : 1;
259        s += rd;
260    }
261
262    return 0;
263}
264
265/** \brief Print a formated string.
266 *
267 *  Format a string at the given coordinates, using the default foreground
268 *  and background values. The coordinates may be outside the canvas
269 *  boundaries (eg. a negative Y coordinate) and the string will be cropped
270 *  accordingly if it is too long. The syntax of the format string is the
271 *  same as for the C printf() function.
272 *
273 *  This function never fails.
274 *
275 *  \param cv A handle to the libcaca canvas.
276 *  \param x X coordinate.
277 *  \param y Y coordinate.
278 *  \param format The format string to print.
279 *  \param ... Arguments to the format string.
280 *  \return This function always returns 0.
281 */
282int caca_printf(caca_canvas_t *cv, int x, int y, char const *format, ...)
283{
284    va_list args;
285    int ret;
286    va_start(args, format);
287    ret = caca_vprintf(cv, x, y, format, args);
288    va_end(args);
289    return ret;
290}
291
292/** \brief Print a formated string (va_list version).
293 *
294 *  Format a string at the given coordinates, using the default foreground
295 *  and background values. The coordinates may be outside the canvas
296 *  boundaries (eg. a negative Y coordinate) and the string will be cropped
297 *  accordingly if it is too long. The syntax of the format string is the
298 *  same as for the C vprintf() function.
299 *
300 *  This function never fails.
301 *
302 *  \param cv A handle to the libcaca canvas.
303 *  \param x X coordinate.
304 *  \param y Y coordinate.
305 *  \param format The format string to print.
306 *  \param ap A va_list containting the arguments to the format string.
307 *  \return This function always returns 0.
308 */
309int caca_vprintf(caca_canvas_t *cv, int x, int y, char const *format,
310                 va_list args)
311{
312    char tmp[BUFSIZ];
313    char *buf = tmp;
314
315    if(y < 0 || y >= (int)cv->height || x >= (int)cv->width)
316        return 0;
317
318    if(cv->width - x + 1 > BUFSIZ)
319        buf = malloc(cv->width - x + 1);
320
321#if defined(HAVE_VSNPRINTF)
322    vsnprintf(buf, cv->width - x + 1, format, args);
323#else
324    vsprintf(buf, format, args);
325#endif
326    buf[cv->width - x] = '\0';
327
328    caca_put_str(cv, x, y, buf);
329
330    if(buf != tmp)
331        free(buf);
332
333    return 0;
334}
335
336/** \brief Clear the canvas.
337 *
338 *  Clear the canvas using the current foreground and background colours.
339 *
340 *  This function never fails.
341 *
342 *  \param cv The canvas to clear.
343 *  \return This function always returns 0.
344 */
345int caca_clear_canvas(caca_canvas_t *cv)
346{
347    uint32_t attr = cv->curattr;
348    int n;
349
350    for(n = cv->width * cv->height; n--; )
351    {
352        cv->chars[n] = (uint32_t)' ';
353        cv->attrs[n] = attr;
354    }
355
356    if(!cv->dirty_disabled)
357        caca_add_dirty_rect(cv, 0, 0, cv->width, cv->height);
358
359    return 0;
360}
361
362/** \brief Set cursor handle.
363 *
364 *  Set the canvas' handle. Blitting functions will use the handle value
365 *  to put the canvas at the proper coordinates.
366 *
367 *  This function never fails.
368 *
369 *  \param cv A handle to the libcaca canvas.
370 *  \param x X handle coordinate.
371 *  \param y Y handle coordinate.
372 *  \return This function always returns 0.
373 */
374int caca_set_canvas_handle(caca_canvas_t *cv, int x, int y)
375{
376    cv->frames[cv->frame].handlex = x;
377    cv->frames[cv->frame].handley = y;
378
379    return 0;
380}
381
382/** \brief Get X handle position.
383 *
384 *  Retrieve the X coordinate of the canvas' handle.
385 *
386 *  This function never fails.
387 *
388 *  \param cv A handle to the libcaca canvas.
389 *  \return The canvas' handle's X coordinate.
390 */
391int caca_get_canvas_handle_x(caca_canvas_t const *cv)
392{
393    return cv->frames[cv->frame].handlex;
394}
395
396/** \brief Get Y handle position.
397 *
398 *  Retrieve the Y coordinate of the canvas' handle.
399 *
400 *  This function never fails.
401 *
402 *  \param cv A handle to the libcaca canvas.
403 *  \return The canvas' handle's Y coordinate.
404 */
405int caca_get_canvas_handle_y(caca_canvas_t const *cv)
406{
407    return cv->frames[cv->frame].handley;
408}
409
410/** \brief Blit a canvas onto another one.
411 *
412 *  Blit a canvas onto another one at the given coordinates.
413 *  An optional mask canvas can be used.
414 *
415 *  If an error occurs, -1 is returned and \b errno is set accordingly:
416 *  - \c EINVAL A mask was specified but the mask size and source canvas
417 *    size do not match.
418 *
419 *  \param dst The destination canvas.
420 *  \param x X coordinate.
421 *  \param y Y coordinate.
422 *  \param src The source canvas.
423 *  \param mask The mask canvas.
424 *  \return 0 in case of success, -1 if an error occurred.
425 */
426int caca_blit(caca_canvas_t *dst, int x, int y,
427              caca_canvas_t const *src, caca_canvas_t const *mask)
428{
429    int i, j, starti, startj, endi, endj, stride, bleed_left, bleed_right;
430
431    if(mask && (src->width != mask->width || src->height != mask->height))
432    {
433        seterrno(EINVAL);
434        return -1;
435    }
436
437    x -= src->frames[src->frame].handlex;
438    y -= src->frames[src->frame].handley;
439
440    starti = x < 0 ? -x : 0;
441    startj = y < 0 ? -y : 0;
442    endi = (x + src->width >= dst->width) ? dst->width - x : src->width;
443    endj = (y + src->height >= dst->height) ? dst->height - y : src->height;
444    stride = endi - starti;
445
446    if(starti > src->width || startj > src->height
447        || starti >= endi || startj >= endj)
448        return 0;
449
450    bleed_left = bleed_right = 0;
451
452    for(j = startj; j < endj; j++)
453    {
454        int dstix = (j + y) * dst->width + starti + x;
455        int srcix = j * src->width + starti;
456
457        /* FIXME: we are ignoring the mask here */
458        if((starti + x) && dst->chars[dstix] == CACA_MAGIC_FULLWIDTH)
459        {
460            dst->chars[dstix - 1] = ' ';
461            bleed_left = 1;
462        }
463
464        if(endi + x < dst->width
465                && dst->chars[dstix + stride] == CACA_MAGIC_FULLWIDTH)
466        {
467            dst->chars[dstix + stride] = ' ';
468            bleed_right = 1;
469        }
470
471        if(mask)
472        {
473            for(i = 0; i < stride; i++)
474            {
475                if(mask->chars[srcix + i] == (uint32_t)' ')
476                    continue;
477
478                if(dst->chars[dstix + i] != src->chars[srcix + i] ||
479                   dst->attrs[dstix + i] != src->attrs[srcix + i])
480                {
481                    dst->chars[dstix + i] = src->chars[srcix + i];
482                    dst->attrs[dstix + i] = src->attrs[srcix + i];
483                    if(!dst->dirty_disabled)
484                        caca_add_dirty_rect(dst, x + starti + i, y + j, 1, 1);
485                }
486            }
487        }
488        else
489        {
490            if(memcmp(dst->chars + dstix, src->chars + srcix, stride * 4) ||
491               memcmp(dst->attrs + dstix, src->attrs + srcix, stride * 4))
492            {
493                /* FIXME be more precise ? */
494                memcpy(dst->chars + dstix, src->chars + srcix, stride * 4);
495                memcpy(dst->attrs + dstix, src->attrs + srcix, stride * 4);
496                if(!dst->dirty_disabled)
497                    caca_add_dirty_rect(dst, x + starti, y + j, stride, 1);
498            }
499        }
500
501        /* Fix split fullwidth chars */
502        if(src->chars[srcix] == CACA_MAGIC_FULLWIDTH)
503            dst->chars[dstix] = ' ';
504
505        if(endi < src->width && src->chars[endi] == CACA_MAGIC_FULLWIDTH)
506            dst->chars[dstix + stride - 1] = ' ';
507    }
508
509
510    return 0;
511}
512
513/** \brief Set a canvas' new boundaries.
514 *
515 *  Set new boundaries for a canvas. This function can be used to crop a
516 *  canvas, to expand it or for combinations of both actions. All frames
517 *  are affected by this function.
518 *
519 *  If an error occurs, -1 is returned and \b errno is set accordingly:
520 *  - \c EINVAL Specified width or height is invalid.
521 *  - \c EBUSY The canvas is in use by a display driver and cannot be resized.
522 *  - \c ENOMEM Not enough memory for the requested canvas size. If this
523 *    happens, the canvas handle becomes invalid and should not be used.
524 *
525 *  \param cv The canvas to crop.
526 *  \param x X coordinate of the top-left corner.
527 *  \param y Y coordinate of the top-left corner.
528 *  \param w The width of the cropped area.
529 *  \param h The height of the cropped area.
530 *  \return 0 in case of success, -1 if an error occurred.
531 */
532int caca_set_canvas_boundaries(caca_canvas_t *cv, int x, int y, int w, int h)
533{
534    caca_canvas_t *new;
535    int f, saved_f, framecount;
536
537    if(cv->refcount)
538    {
539        seterrno(EBUSY);
540        return -1;
541    }
542
543    if(w < 0 || h < 0)
544    {
545        seterrno(EINVAL);
546        return -1;
547    }
548
549    new = caca_create_canvas(w, h);
550
551    framecount = caca_get_frame_count(cv);
552    saved_f = cv->frame;
553
554    for(f = 0; f < framecount; f++)
555    {
556        if(f)
557            caca_create_frame(new, framecount);
558
559        caca_set_frame(cv, f);
560        caca_set_frame(new, f);
561        caca_blit(new, -x, -y, cv, NULL);
562        free(cv->frames[f].chars);
563        free(cv->frames[f].attrs);
564    }
565    free(cv->frames);
566
567    cv->frames = new->frames;
568    free(new);
569
570    caca_set_frame(cv, saved_f);
571    _caca_load_frame_info(cv);
572
573    /* FIXME: this may be optimised somewhat */
574    if(!cv->dirty_disabled)
575        caca_add_dirty_rect(cv, 0, 0, cv->width, cv->height);
576
577    return 0;
578}
579
580/*
581 * XXX: The following functions are aliases.
582 */
583
584int cucul_gotoxy(cucul_canvas_t *, int, int) CACA_ALIAS(caca_gotoxy);
585int cucul_get_cursor_x(cucul_canvas_t const *) CACA_ALIAS(caca_get_cursor_x);
586int cucul_get_cursor_y(cucul_canvas_t const *) CACA_ALIAS(caca_get_cursor_y);
587int cucul_put_char(cucul_canvas_t *, int, int, uint32_t)
588         CACA_ALIAS(caca_put_char);
589uint32_t cucul_get_char(cucul_canvas_t const *, int, int)
590         CACA_ALIAS(caca_get_char);
591int cucul_put_str(cucul_canvas_t *, int, int, char const *)
592         CACA_ALIAS(caca_put_str);
593int cucul_printf(cucul_canvas_t *, int, int, char const *, ...)
594         CACA_ALIAS(caca_printf);
595int cucul_clear_canvas(cucul_canvas_t *) CACA_ALIAS(caca_clear_canvas);
596int cucul_set_canvas_handle(cucul_canvas_t *, int, int)
597         CACA_ALIAS(caca_set_canvas_handle);
598int cucul_get_canvas_handle_x(cucul_canvas_t const *)
599         CACA_ALIAS(caca_get_canvas_handle_x);
600int cucul_get_canvas_handle_y(cucul_canvas_t const *)
601         CACA_ALIAS(caca_get_canvas_handle_y);
602int cucul_blit(cucul_canvas_t *, int, int, cucul_canvas_t const *,
603                        cucul_canvas_t const *) CACA_ALIAS(caca_blit);
604int cucul_set_canvas_boundaries(cucul_canvas_t *, int, int, int, int)
605         CACA_ALIAS(caca_set_canvas_boundaries);
606
Note: See TracBrowser for help on using the repository browser.