268 lines
7.3 KiB
C
268 lines
7.3 KiB
C
/*
|
|
* libcaca Colour ASCII-Art library
|
|
* Copyright © 2002—2018 Sam Hocevar <sam@hocevar.net>
|
|
* All Rights Reserved
|
|
*
|
|
* This library is free software. It comes without any warranty, to
|
|
* the extent permitted by applicable law. You can redistribute it
|
|
* and/or modify it under the terms of the Do What the Fuck You Want
|
|
* to Public License, Version 2, as published by Sam Hocevar. See
|
|
* http://www.wtfpl.net/ for more details.
|
|
*/
|
|
|
|
/*
|
|
* This file contains a small framework for canvas frame management.
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#if !defined(__KERNEL__)
|
|
# include <stdio.h>
|
|
# include <stdlib.h>
|
|
# include <string.h>
|
|
#endif
|
|
|
|
#include "caca.h"
|
|
#include "caca_internals.h"
|
|
|
|
/** \brief Get the number of frames in a canvas.
|
|
*
|
|
* Return the current canvas' frame count.
|
|
*
|
|
* This function never fails.
|
|
*
|
|
* \param cv A libcaca canvas
|
|
* \return The frame count
|
|
*/
|
|
int caca_get_frame_count(caca_canvas_t const *cv)
|
|
{
|
|
return cv->framecount;
|
|
}
|
|
|
|
/** \brief Activate a given canvas frame.
|
|
*
|
|
* Set the active canvas frame. All subsequent drawing operations will
|
|
* be performed on that frame. The current painting context set by
|
|
* caca_set_attr() is inherited.
|
|
*
|
|
* If the frame index is outside the canvas' frame range, nothing happens.
|
|
*
|
|
* If an error occurs, -1 is returned and \b errno is set accordingly:
|
|
* - \c EINVAL Requested frame is out of range.
|
|
*
|
|
* \param cv A libcaca canvas
|
|
* \param id The canvas frame to activate
|
|
* \return 0 in case of success, -1 if an error occurred.
|
|
*/
|
|
int caca_set_frame(caca_canvas_t *cv, int id)
|
|
{
|
|
if(id < 0 || id >= cv->framecount)
|
|
{
|
|
seterrno(EINVAL);
|
|
return -1;
|
|
}
|
|
|
|
/* Bail out if no operation is required */
|
|
if(id == cv->frame)
|
|
return 0;
|
|
|
|
_caca_save_frame_info(cv);
|
|
cv->frame = id;
|
|
_caca_load_frame_info(cv);
|
|
|
|
if(!cv->dirty_disabled)
|
|
caca_add_dirty_rect(cv, 0, 0, cv->width, cv->height);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/** \brief Get the current frame's name.
|
|
*
|
|
* Return the current frame's name. The returned string is valid until
|
|
* the frame is deleted or caca_set_frame_name() is called to change
|
|
* the frame name again.
|
|
*
|
|
* This function never fails.
|
|
*
|
|
* \param cv A libcaca canvas.
|
|
* \return The current frame's name.
|
|
*/
|
|
char const *caca_get_frame_name(caca_canvas_t const *cv)
|
|
{
|
|
return cv->frames[cv->frame].name;
|
|
}
|
|
|
|
/** \brief Set the current frame's name.
|
|
*
|
|
* Set the current frame's name. Upon creation, a frame has a default name
|
|
* of \c "frame#xxxxxxxx" where \c xxxxxxxx is a self-incrementing
|
|
* hexadecimal number.
|
|
*
|
|
* If an error occurs, -1 is returned and \b errno is set accordingly:
|
|
* - \c ENOMEM Not enough memory to allocate new frame.
|
|
*
|
|
* \param cv A libcaca canvas.
|
|
* \param name The name to give to the current frame.
|
|
* \return 0 in case of success, -1 if an error occurred.
|
|
*/
|
|
int caca_set_frame_name(caca_canvas_t *cv, char const *name)
|
|
{
|
|
char *newname = strdup(name);
|
|
|
|
if(!newname)
|
|
{
|
|
seterrno(ENOMEM);
|
|
return -1;
|
|
}
|
|
|
|
free(cv->frames[cv->frame].name);
|
|
cv->frames[cv->frame].name = newname;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/** \brief Add a frame to a canvas.
|
|
*
|
|
* Create a new frame within the given canvas. Its contents and attributes
|
|
* are copied from the currently active frame.
|
|
*
|
|
* The frame index indicates where the frame should be inserted. Valid
|
|
* values range from 0 to the current canvas frame count. If the frame
|
|
* index is greater than or equals the current canvas frame count, the new
|
|
* frame is appended at the end of the canvas. If the frame index is less
|
|
* than zero, the new frame is inserted at index 0.
|
|
*
|
|
* The active frame does not change, but its index may be renumbered due
|
|
* to the insertion.
|
|
*
|
|
* If an error occurs, -1 is returned and \b errno is set accordingly:
|
|
* - \c ENOMEM Not enough memory to allocate new frame.
|
|
*
|
|
* \param cv A libcaca canvas
|
|
* \param id The index where to insert the new frame
|
|
* \return 0 in case of success, -1 if an error occurred.
|
|
*/
|
|
int caca_create_frame(caca_canvas_t *cv, int id)
|
|
{
|
|
int size = cv->width * cv->height;
|
|
int f;
|
|
|
|
if(id < 0)
|
|
id = 0;
|
|
else if(id > cv->framecount)
|
|
id = cv->framecount;
|
|
|
|
cv->framecount++;
|
|
cv->frames = realloc(cv->frames,
|
|
sizeof(struct caca_frame) * cv->framecount);
|
|
|
|
for(f = cv->framecount - 1; f > id; f--)
|
|
cv->frames[f] = cv->frames[f - 1];
|
|
|
|
if(cv->frame >= id)
|
|
cv->frame++;
|
|
|
|
cv->frames[id].width = cv->width;
|
|
cv->frames[id].height = cv->height;
|
|
cv->frames[id].chars = malloc(size * sizeof(uint32_t));
|
|
memcpy(cv->frames[id].chars, cv->chars, size * sizeof(uint32_t));
|
|
cv->frames[id].attrs = malloc(size * sizeof(uint32_t));
|
|
memcpy(cv->frames[id].attrs, cv->attrs, size * sizeof(uint32_t));
|
|
cv->frames[id].curattr = cv->curattr;
|
|
|
|
cv->frames[id].x = cv->frames[cv->frame].x;
|
|
cv->frames[id].y = cv->frames[cv->frame].y;
|
|
cv->frames[id].handlex = cv->frames[cv->frame].handlex;
|
|
cv->frames[id].handley = cv->frames[cv->frame].handley;
|
|
|
|
cv->frames[id].name = strdup("frame#--------");
|
|
sprintf(cv->frames[id].name + 6, "%.08x", ++cv->autoinc);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/** \brief Remove a frame from a canvas.
|
|
*
|
|
* Delete a frame from a given canvas.
|
|
*
|
|
* The frame index indicates the frame to delete. Valid values range from
|
|
* 0 to the current canvas frame count minus 1. If the frame index is
|
|
* greater than or equals the current canvas frame count, the last frame
|
|
* is deleted.
|
|
*
|
|
* If the active frame is deleted, frame 0 becomes the new active frame.
|
|
* Otherwise, the active frame does not change, but its index may be
|
|
* renumbered due to the deletion.
|
|
*
|
|
* If an error occurs, -1 is returned and \b errno is set accordingly:
|
|
* - \c EINVAL Requested frame is out of range, or attempt to delete the
|
|
* last frame of the canvas.
|
|
*
|
|
* \param cv A libcaca canvas
|
|
* \param id The index of the frame to delete
|
|
* \return 0 in case of success, -1 if an error occurred.
|
|
*/
|
|
int caca_free_frame(caca_canvas_t *cv, int id)
|
|
{
|
|
int f;
|
|
|
|
if(id < 0 || id >= cv->framecount)
|
|
{
|
|
seterrno(EINVAL);
|
|
return -1;
|
|
}
|
|
|
|
if(cv->framecount == 1)
|
|
{
|
|
seterrno(EINVAL);
|
|
return -1;
|
|
}
|
|
|
|
free(cv->frames[id].chars);
|
|
free(cv->frames[id].attrs);
|
|
free(cv->frames[id].name);
|
|
|
|
for(f = id + 1; f < cv->framecount; f++)
|
|
cv->frames[f - 1] = cv->frames[f];
|
|
|
|
cv->framecount--;
|
|
cv->frames = realloc(cv->frames,
|
|
sizeof(struct caca_frame) * cv->framecount);
|
|
|
|
if(cv->frame > id)
|
|
cv->frame--;
|
|
else if(cv->frame == id)
|
|
{
|
|
cv->frame = 0;
|
|
_caca_load_frame_info(cv);
|
|
if(!cv->dirty_disabled)
|
|
caca_add_dirty_rect(cv, 0, 0, cv->width, cv->height);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* XXX: the following functions are local.
|
|
*/
|
|
|
|
void _caca_save_frame_info(caca_canvas_t *cv)
|
|
{
|
|
cv->frames[cv->frame].width = cv->width;
|
|
cv->frames[cv->frame].height = cv->height;
|
|
|
|
cv->frames[cv->frame].curattr = cv->curattr;
|
|
}
|
|
|
|
void _caca_load_frame_info(caca_canvas_t *cv)
|
|
{
|
|
cv->width = cv->frames[cv->frame].width;
|
|
cv->height = cv->frames[cv->frame].height;
|
|
|
|
cv->chars = cv->frames[cv->frame].chars;
|
|
cv->attrs = cv->frames[cv->frame].attrs;
|
|
|
|
cv->curattr = cv->frames[cv->frame].curattr;
|
|
}
|
|
|