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

Last change on this file since 3595 was 3595, checked in by Sam Hocevar, 10 years ago

Make caca_printf(), caca_vprintf() and caca_put_str() return the number of
printed cells instead of always returning 0. This is handy if we want to
advance a cursor after each printf() call.

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