/* * libcaca Colour ASCII-Art library * Copyright © 2002—2018 Sam Hocevar * 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 # include # include #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; }