/** \brief Flip a canvas vertically. * * Flip a canvas vertically, choosing characters that look like the * mirrored version wherever possible. Some characters will stay * unchanged by the process, but the operation is guaranteed to be * involutive: performing it again gives back the original canvas. * * This function never fails. * * \param cv The canvas to flop. * \return This function always returns 0. */ int caca_flop(caca_canvas_t *cv) { int x; for(x = 0; x < cv->width; x++) { uint32_t *ctop = cv->chars + x; uint32_t *cbottom = ctop + cv->width * (cv->height - 1); uint32_t *atop = cv->attrs + x; uint32_t *abottom = atop + cv->width * (cv->height - 1); while(ctop < cbottom) { uint32_t ch; uint32_t attr; /* Swap attributes */ attr = *abottom; *abottom = *atop; *atop = attr; /* Swap characters */ ch = *cbottom; *cbottom = flopchar(*ctop); *ctop = flopchar(ch); ctop += cv->width; cbottom -= cv->width; atop += cv->width; abottom -= cv->width; } if(ctop == cbottom) *ctop = flopchar(*ctop); } if(!cv->dirty_disabled) caca_add_dirty_rect(cv, 0, 0, cv->width, cv->height); return 0; }
static int vga_init_graphics(caca_display_t *dp) { int i; uint8_t tmp; /* Blank screen */ memset(VGA_SCREEN, 0, 80 * 25 * 2); /* Fill VGA palette */ for(i = 0; i < 16; i++) { outb(vga_colors[i][0], 0x3c8); outb(vga_colors[i][1], 0x3c9); outb(vga_colors[i][2], 0x3c9); outb(vga_colors[i][3], 0x3c9); } /* Hide cursor */ outb(0x0a, 0x3d4); tmp = inb(0x3d5); tmp |= 0x20; outb(0x0a, 0x3d4); outb(tmp, 0x3d5); caca_add_dirty_rect(dp->cv, 0, 0, dp->cv->width, dp->cv->height); dp->resize.allow = 1; caca_set_canvas_size(dp->cv, 80, 25); /* We don't have much choice */ dp->resize.allow = 0; return 0; }
/** \brief Fill a box on the canvas using the given character. * * This function never fails. * * \param cv The handle to the libcaca canvas. * \param x X coordinate of the upper-left corner of the box. * \param y Y coordinate of the upper-left corner of the box. * \param w Width of the box. * \param h Height of the box. * \param ch UTF-32 character to be used to draw the box. * \return This function always returns 0. */ int caca_fill_box(caca_canvas_t *cv, int x, int y, int w, int h, uint32_t ch) { int i, j, xmax, ymax; int x2 = x + w - 1; int y2 = y + h - 1; if(x > x2) { int tmp = x; x = x2; x2 = tmp; } if(y > y2) { int tmp = y; y = y2; y2 = tmp; } xmax = cv->width - 1; ymax = cv->height - 1; if(x2 < 0 || y2 < 0 || x > xmax || y > ymax) return 0; if(x < 0) x = 0; if(y < 0) y = 0; if(x2 > xmax) x2 = xmax; if(y2 > ymax) y2 = ymax; #if 0 /* FIXME: this fails with fullwidth character blits. Also, the dirty * rectangle handling may miss fullwidth cells. */ /* Optimise dirty rectangle handling, part 1 */ cv->dirty_disabled++; #endif for(j = y; j <= y2; j++) for(i = x; i <= x2; i++) caca_put_char(cv, i, j, ch); #if 0 /* Optimise dirty rectangle handling, part 2 */ cv->dirty_disabled--; if(!cv->dirty_disabled) caca_add_dirty_rect(cv, x, y, x2 - x + 1, y2 - y + 1); #endif return 0; }
/** \brief Flip a canvas horizontally. * * Flip a canvas horizontally, choosing characters that look like the * mirrored version wherever possible. Some characters will stay * unchanged by the process, but the operation is guaranteed to be * involutive: performing it again gives back the original canvas. * * This function never fails. * * \param cv The canvas to flip. * \return This function always returns 0. */ int caca_flip(caca_canvas_t *cv) { int y; for(y = 0; y < cv->height; y++) { uint32_t *cleft = cv->chars + y * cv->width; uint32_t *cright = cleft + cv->width - 1; uint32_t *aleft = cv->attrs + y * cv->width; uint32_t *aright = aleft + cv->width - 1; while(cleft < cright) { uint32_t ch; uint32_t attr; /* Swap attributes */ attr = *aright; *aright-- = *aleft; *aleft++ = attr; /* Swap characters */ ch = *cright; *cright-- = flipchar(*cleft); *cleft++ = flipchar(ch); } if(cleft == cright) *cleft = flipchar(*cleft); /* Fix fullwidth characters. Could it be done in one loop? */ cleft = cv->chars + y * cv->width; cright = cleft + cv->width - 1; for( ; cleft < cright; cleft++) { if(cleft[0] == CACA_MAGIC_FULLWIDTH) { cleft[0] = cleft[1]; cleft[1] = CACA_MAGIC_FULLWIDTH; cleft++; } } } if(!cv->dirty_disabled) caca_add_dirty_rect(cv, 0, 0, cv->width, cv->height); return 0; }
/** \brief Rotate a canvas. * * Apply a 180-degree transformation to a canvas, choosing characters * that look like the upside-down version wherever possible. Some * characters will stay unchanged by the process, but the operation is * guaranteed to be involutive: performing it again gives back the * original canvas. * * This function never fails. * * \param cv The canvas to rotate. * \return This function always returns 0. */ int caca_rotate_180(caca_canvas_t *cv) { uint32_t *cbegin = cv->chars; uint32_t *cend = cbegin + cv->width * cv->height - 1; uint32_t *abegin = cv->attrs; uint32_t *aend = abegin + cv->width * cv->height - 1; int y; if(!cbegin) return 0; while(cbegin < cend) { uint32_t ch; uint32_t attr; /* Swap attributes */ attr = *aend; *aend = *abegin; *abegin = attr; /* Swap characters */ ch = *cend; *cend = rotatechar(*cbegin); *cbegin = rotatechar(ch); cbegin++; cend--; abegin++; aend--; } if(cbegin == cend) *cbegin = rotatechar(*cbegin); /* Fix fullwidth characters. Could it be done in one loop? */ for(y = 0; y < cv->height; y++) { cbegin = cv->chars + y * cv->width; cend = cbegin + cv->width - 1; for( ; cbegin < cend; cbegin++) { if(cbegin[0] == CACA_MAGIC_FULLWIDTH) { cbegin[0] = cbegin[1]; cbegin[1] = CACA_MAGIC_FULLWIDTH; cbegin++; } } } if(!cv->dirty_disabled) caca_add_dirty_rect(cv, 0, 0, cv->width, cv->height); return 0; }
/** \brief Clear the canvas. * * Clear the canvas using the current foreground and background colours. * * This function never fails. * * \param cv The canvas to clear. * \return This function always returns 0. */ int caca_clear_canvas(caca_canvas_t *cv) { uint32_t attr = cv->curattr; int n; for(n = cv->width * cv->height; n--; ) { cv->chars[n] = (uint32_t)' '; cv->attrs[n] = attr; } if(!cv->dirty_disabled) caca_add_dirty_rect(cv, 0, 0, cv->width, cv->height); return 0; }
/** \brief Invert a canvas' colours. * * Invert a canvas' colours (black becomes white, red becomes cyan, etc.) * without changing the characters in it. * * This function never fails. * * \param cv The canvas to invert. * \return This function always returns 0. */ int caca_invert(caca_canvas_t *cv) { uint32_t *attrs = cv->attrs; int i; for(i = cv->height * cv->width; i--; ) { *attrs = *attrs ^ 0x000f000f; attrs++; } if(!cv->dirty_disabled) caca_add_dirty_rect(cv, 0, 0, cv->width, cv->height); return 0; }
/** \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 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; }
/** \brief Blit a canvas onto another one. * * Blit a canvas onto another one at the given coordinates. * An optional mask canvas can be used. * * If an error occurs, -1 is returned and \b errno is set accordingly: * - \c EINVAL A mask was specified but the mask size and source canvas * size do not match. * * \param dst The destination canvas. * \param x X coordinate. * \param y Y coordinate. * \param src The source canvas. * \param mask The mask canvas. * \return 0 in case of success, -1 if an error occurred. */ int caca_blit(caca_canvas_t *dst, int x, int y, caca_canvas_t const *src, caca_canvas_t const *mask) { int i, j, starti, startj, endi, endj, stride, bleed_left, bleed_right; if(mask && (src->width != mask->width || src->height != mask->height)) { seterrno(EINVAL); return -1; } x -= src->frames[src->frame].handlex; y -= src->frames[src->frame].handley; starti = x < 0 ? -x : 0; startj = y < 0 ? -y : 0; endi = (x + src->width >= dst->width) ? dst->width - x : src->width; endj = (y + src->height >= dst->height) ? dst->height - y : src->height; stride = endi - starti; if(starti > src->width || startj > src->height || starti >= endi || startj >= endj) return 0; bleed_left = bleed_right = 0; for(j = startj; j < endj; j++) { int dstix = (j + y) * dst->width + starti + x; int srcix = j * src->width + starti; /* FIXME: we are ignoring the mask here */ if((starti + x) && dst->chars[dstix] == CACA_MAGIC_FULLWIDTH) { dst->chars[dstix - 1] = ' '; bleed_left = 1; } if(endi + x < dst->width && dst->chars[dstix + stride] == CACA_MAGIC_FULLWIDTH) { dst->chars[dstix + stride] = ' '; bleed_right = 1; } if(mask) { for(i = 0; i < stride; i++) { if(mask->chars[srcix + i] == (uint32_t)' ') continue; if(dst->chars[dstix + i] != src->chars[srcix + i] || dst->attrs[dstix + i] != src->attrs[srcix + i]) { dst->chars[dstix + i] = src->chars[srcix + i]; dst->attrs[dstix + i] = src->attrs[srcix + i]; if(!dst->dirty_disabled) caca_add_dirty_rect(dst, x + starti + i, y + j, 1, 1); } } } else { if(memcmp(dst->chars + dstix, src->chars + srcix, stride * 4) || memcmp(dst->attrs + dstix, src->attrs + srcix, stride * 4)) { /* FIXME be more precise ? */ memcpy(dst->chars + dstix, src->chars + srcix, stride * 4); memcpy(dst->attrs + dstix, src->attrs + srcix, stride * 4); if(!dst->dirty_disabled) caca_add_dirty_rect(dst, x + starti, y + j, stride, 1); } } /* Fix split fullwidth chars */ if(src->chars[srcix] == CACA_MAGIC_FULLWIDTH) dst->chars[dstix] = ' '; if(endi < src->width && src->chars[endi] == CACA_MAGIC_FULLWIDTH) dst->chars[dstix + stride - 1] = ' '; } return 0; }
/** \brief Print an ASCII or Unicode character. * * Print an ASCII or Unicode character at the given coordinates, using * the default foreground and background colour values. * * If the coordinates are outside the canvas boundaries, nothing is printed. * If a fullwidth Unicode character gets overwritten, its remaining visible * parts are replaced with spaces. If the canvas' boundaries would split the * fullwidth character in two, a space is printed instead. * * The behaviour when printing non-printable characters or invalid UTF-32 * characters is undefined. To print a sequence of bytes forming an UTF-8 * character instead of an UTF-32 character, use the caca_put_str() function. * * This function returns the width of the printed character. If it is a * fullwidth character, 2 is returned. Otherwise, 1 is returned. * * This function never fails. * * \param cv A handle to the libcaca canvas. * \param x X coordinate. * \param y Y coordinate. * \param ch The character to print. * \return The width of the printed character: 2 for a fullwidth character, * 1 otherwise. */ int caca_put_char(caca_canvas_t *cv, int x, int y, uint32_t ch) { uint32_t *curchar, *curattr, attr; int fullwidth, xmin, xmax, ret; if(ch == CACA_MAGIC_FULLWIDTH) return 1; fullwidth = caca_utf32_is_fullwidth(ch); ret = fullwidth ? 2 : 1; if(x >= (int)cv->width || y < 0 || y >= (int)cv->height) return ret; if(x == -1 && fullwidth) { x = 0; ch = ' '; fullwidth = 0; } else if(x < 0) return ret; curchar = cv->chars + x + y * cv->width; curattr = cv->attrs + x + y * cv->width; attr = cv->curattr; xmin = xmax = x; /* When overwriting the right part of a fullwidth character, * replace its left part with a space. */ if(x && curchar[0] == CACA_MAGIC_FULLWIDTH) { curchar[-1] = ' '; xmin--; } if(fullwidth) { if(x + 1 == (int)cv->width) ch = ' '; else { xmax++; /* When overwriting the left part of a fullwidth character, * replace its right part with a space. */ if(x + 2 < (int)cv->width && curchar[2] == CACA_MAGIC_FULLWIDTH) { curchar[2] = ' '; xmax++; } curchar[1] = CACA_MAGIC_FULLWIDTH; curattr[1] = attr; } } else { /* When overwriting the left part of a fullwidth character, * replace its right part with a space. */ if(x + 1 != (int)cv->width && curchar[1] == CACA_MAGIC_FULLWIDTH) { curchar[1] = ' '; xmax++; } } /* Only add a dirty rectangle if we are pasting a different character * or attribute at that place. This does not account for inconsistencies * in the canvas, ie. if CACA_MAGIC_FULLWIDTH lies at illegal places, * but it's the caller's responsibility not to corrupt the contents. */ if(!cv->dirty_disabled && (curchar[0] != ch || curattr[0] != attr)) caca_add_dirty_rect(cv, xmin, y, xmax - xmin + 1, 1); curchar[0] = ch; curattr[0] = attr; return ret; }
int caca_resize(caca_canvas_t *cv, int width, int height) { int x, y, f, old_width, old_height, new_size, old_size; old_width = cv->width; old_height = cv->height; old_size = old_width * old_height; _caca_save_frame_info(cv); /* Preload new width and height values into the canvas to optimise * dirty rectangle handling */ cv->width = width; cv->height = height; new_size = width * height; /* If width or height is smaller (or both), we have the opportunity to * reduce or even remove dirty rectangles */ if(width < old_width || height < old_height) _caca_clip_dirty_rect_list(cv); /* Step 1: if new area is bigger, resize the memory area now. */ if(new_size > old_size) { for(f = 0; f < cv->framecount; f++) { cv->frames[f].chars = realloc(cv->frames[f].chars, new_size * sizeof(uint32_t)); cv->frames[f].attrs = realloc(cv->frames[f].attrs, new_size * sizeof(uint32_t)); if(new_size && (!cv->frames[f].chars || !cv->frames[f].attrs)) { seterrno(ENOMEM); return -1; } } } /* Step 2: move line data if necessary. */ if(width == old_width) { /* Width did not change, which means we do not need to move data. */ ; } else if(width > old_width) { /* New width is bigger than old width, which means we need to * copy lines starting from the bottom of the screen otherwise * we will overwrite information. */ for(f = 0; f < cv->framecount; f++) { uint32_t *chars = cv->frames[f].chars; uint32_t *attrs = cv->frames[f].attrs; for(y = height < old_height ? height : old_height; y--; ) { uint32_t attr = cv->frames[f].curattr; for(x = old_width; x--; ) { chars[y * width + x] = chars[y * old_width + x]; attrs[y * width + x] = attrs[y * old_width + x]; } /* Zero the end of the line */ for(x = width - old_width; x--; ) { chars[y * width + old_width + x] = (uint32_t)' '; attrs[y * width + old_width + x] = attr; } } } if(!cv->dirty_disabled) caca_add_dirty_rect(cv, old_width, 0, width - old_width, old_height); } else { /* New width is smaller. Copy as many lines as possible. Ignore * the first line, it is already in place. */ int lines = height < old_height ? height : old_height; for(f = 0; f < cv->framecount; f++) { uint32_t *chars = cv->frames[f].chars; uint32_t *attrs = cv->frames[f].attrs; for(y = 1; y < lines; y++) { for(x = 0; x < width; x++) { chars[y * width + x] = chars[y * old_width + x]; attrs[y * width + x] = attrs[y * old_width + x]; } } } } /* Step 3: fill the bottom of the new screen if necessary. */ if(height > old_height) { for(f = 0; f < cv->framecount; f++) { uint32_t *chars = cv->frames[f].chars; uint32_t *attrs = cv->frames[f].attrs; uint32_t attr = cv->frames[f].curattr; /* Zero the bottom of the screen */ for(x = (height - old_height) * width; x--; ) { chars[old_height * width + x] = (uint32_t)' '; attrs[old_height * width + x] = attr; } } if(!cv->dirty_disabled) caca_add_dirty_rect(cv, 0, old_height, old_width, height - old_height); } /* If both width and height are larger, there is a new dirty rectangle * that needs to be created in the lower right corner. */ if(!cv->dirty_disabled && width > old_width && height > old_height) caca_add_dirty_rect(cv, old_width, old_height, width - old_width, height - old_height); /* Step 4: if new area is smaller, resize memory area now. */ if(new_size < old_size) { for(f = 0; f < cv->framecount; f++) { cv->frames[f].chars = realloc(cv->frames[f].chars, new_size * sizeof(uint32_t)); cv->frames[f].attrs = realloc(cv->frames[f].attrs, new_size * sizeof(uint32_t)); if(new_size && (!cv->frames[f].chars || !cv->frames[f].attrs)) { seterrno(ENOMEM); return -1; } } } /* Set new size */ for(f = 0; f < cv->framecount; f++) { if(cv->frames[f].x > (int)width) cv->frames[f].x = width; if(cv->frames[f].y > (int)height) cv->frames[f].y = height; cv->frames[f].width = width; cv->frames[f].height = height; } /* Reset the current frame shortcuts */ _caca_load_frame_info(cv); return 0; }
static int ncurses_init_graphics(caca_display_t *dp) { static int curses_colors[] = { /* Standard curses colours */ COLOR_BLACK, COLOR_BLUE, COLOR_GREEN, COLOR_CYAN, COLOR_RED, COLOR_MAGENTA, COLOR_YELLOW, COLOR_WHITE, /* Extra values for xterm-16color */ COLOR_BLACK + 8, COLOR_BLUE + 8, COLOR_GREEN + 8, COLOR_CYAN + 8, COLOR_RED + 8, COLOR_MAGENTA + 8, COLOR_YELLOW + 8, COLOR_WHITE + 8 }; mmask_t newmask; int fg, bg, max; dp->drv.p = malloc(sizeof(struct driver_private)); #if defined HAVE_GETENV && defined HAVE_PUTENV ncurses_install_terminal(dp); #endif #if defined HAVE_SIGNAL sigwinch_d = dp; signal(SIGWINCH, sigwinch_handler); #endif #if defined HAVE_LOCALE_H setlocale(LC_ALL, ""); #endif _caca_set_term_title("caca for ncurses"); initscr(); keypad(stdscr, TRUE); nonl(); raw(); noecho(); nodelay(stdscr, TRUE); curs_set(0); /* Activate mouse */ newmask = REPORT_MOUSE_POSITION | ALL_MOUSE_EVENTS; mousemask(newmask, &dp->drv.p->oldmask); mouseinterval(-1); /* No click emulation */ /* Set the escape delay to a ridiculously low value */ ESCDELAY = 10; /* Activate colour */ start_color(); /* If COLORS == 16, it means the terminal supports full bright colours * using setab and setaf (will use \e[90m \e[91m etc. for colours >= 8), * we can build 16*16 colour pairs. * If COLORS == 8, it means the terminal does not know about bright * colours and we need to get them through A_BOLD and A_BLINK (\e[1m * and \e[5m). We can only build 8*8 colour pairs. */ max = COLORS >= 16 ? 16 : 8; for(bg = 0; bg < max; bg++) for(fg = 0; fg < max; fg++) { /* Use ((max + 7 - fg) % max) instead of fg so that colour 0 * is light gray on black. Some terminals don't like this * colour pair to be redefined. */ int col = ((max + 7 - fg) % max) + max * bg; init_pair(col, curses_colors[fg], curses_colors[bg]); dp->drv.p->attr[fg + 16 * bg] = COLOR_PAIR(col); if(max == 8) { /* Bright fg on simple bg */ dp->drv.p->attr[fg + 8 + 16 * bg] = A_BOLD | COLOR_PAIR(col); /* Simple fg on bright bg */ dp->drv.p->attr[fg + 16 * (bg + 8)] = A_BLINK | COLOR_PAIR(col); /* Bright fg on bright bg */ dp->drv.p->attr[fg + 8 + 16 * (bg + 8)] = A_BLINK | A_BOLD | COLOR_PAIR(col); } } caca_add_dirty_rect(dp->cv, 0, 0, dp->cv->width, dp->cv->height); dp->resize.allow = 1; caca_set_canvas_size(dp->cv, COLS, LINES); dp->resize.allow = 0; return 0; }
static int slang_init_graphics(caca_display_t *dp) { dp->drv.p = malloc(sizeof(struct driver_private)); dp->drv.p->sigint_event = 0; #if defined(HAVE_GETENV) && defined(HAVE_PUTENV) slang_install_terminal(dp); #endif #if defined(HAVE_SIGNAL) sigwinch_d = dp; signal(SIGWINCH, sigwinch_handler); #endif _caca_set_term_title("caca for S-Lang"); /* Initialise slang library */ SLsig_block_signals(); /* Disable SLang's own SIGINT on ctrl-c */ SLang_set_abort_signal(default_sigint); SLtt_get_terminfo(); if(SLkp_init() == -1) { SLsig_unblock_signals(); return -1; } SLang_init_tty(-1, 0, 1); if(SLsmg_init_smg() == -1) { SLsig_unblock_signals(); return -1; } SLsmg_cls(); SLtt_set_cursor_visibility(0); SLkp_define_keysym("\e[M", 1001); SLtt_set_mouse_mode(1, 0); SLsmg_refresh(); /* Disable scrolling so that hashmap scrolling optimization code * does not cause ugly refreshes due to slow terminals */ SLtt_Term_Cannot_Scroll = 1; slang_init_palette(); #if defined(VMS) || defined(REAL_UNIX_SYSTEM) /* Disable alt charset support so that we get a chance to have all * 256 colour pairs */ SLtt_Has_Alt_Charset = 0; #endif #ifdef HAVE_SLSMG_UTF8_ENABLE SLsmg_utf8_enable(1); /* 1 == force, 0 == disable, -1 == autodetect */ SLtt_utf8_enable(1); #endif caca_add_dirty_rect(dp->cv, 0, 0, dp->cv->width, dp->cv->height); dp->resize.allow = 1; caca_set_canvas_size(dp->cv, SLtt_Screen_Cols, SLtt_Screen_Rows); dp->resize.allow = 0; SLsig_unblock_signals(); return 0; }
/** \brief Rotate and stretch a canvas, 90 degrees clockwise. * * Apply a 270-degree transformation to a canvas, choosing characters * that look like the rotated version wherever possible. Some characters * will stay unchanged by the process, some others will be replaced by * close equivalents. Fullwidth characters will be lost. The operation is * not guaranteed to be reversible at all. * * Note that the width and height of the canvas are swapped, causing its * aspect ratio to look stretched. * * If an error occurs, -1 is returned and \b errno is set accordingly: * - \c EBUSY The canvas is in use by a display driver and cannot be rotated. * - \c ENOMEM Not enough memory to allocate the new canvas size. If this * happens, the previous canvas handle is still valid. * * \param cv The canvas to rotate right. * \return 0 in case of success, -1 if an error occurred. */ int caca_stretch_right(caca_canvas_t *cv) { uint32_t *newchars, *newattrs; int x, y; if(cv->refcount) { seterrno(EBUSY); return -1; } /* Save the current frame shortcuts */ _caca_save_frame_info(cv); newchars = malloc(cv->width * cv->height * sizeof(uint32_t)); if(!newchars) { seterrno(ENOMEM); return -1; } newattrs = malloc(cv->width * cv->height * sizeof(uint32_t)); if(!newattrs) { free(newchars); seterrno(ENOMEM); return -1; } for(y = 0; y < cv->height; y++) { for(x = 0; x < cv->width; x++) { uint32_t ch, attr; ch = cv->chars[cv->width * y + x]; attr = cv->attrs[cv->width * y + x]; /* FIXME: do something about fullwidth characters */ ch = rightchar(ch); newchars[cv->height * x + cv->height - 1 - y] = ch; newattrs[cv->height * x + cv->height - 1 - y] = attr; } } free(cv->chars); free(cv->attrs); /* Swap X and Y information */ x = cv->frames[cv->frame].x; y = cv->frames[cv->frame].y; cv->frames[cv->frame].x = cv->height - 1 - y; cv->frames[cv->frame].y = x; x = cv->frames[cv->frame].handlex; y = cv->frames[cv->frame].handley; cv->frames[cv->frame].handlex = cv->height - 1 - y; cv->frames[cv->frame].handley = x; cv->frames[cv->frame].width = cv->height; cv->frames[cv->frame].height = cv->width; cv->frames[cv->frame].chars = newchars; cv->frames[cv->frame].attrs = newattrs; /* Reset the current frame shortcuts */ _caca_load_frame_info(cv); caca_add_dirty_rect(cv, 0, 0, cv->width, cv->height); return 0; }
/** \brief Rotate a canvas, 90 degrees counterclockwise. * * Apply a 90-degree transformation to a canvas, choosing characters * that look like the rotated version wherever possible. Characters cells * are rotated two-by-two. Some characters will stay unchanged by the * process, some others will be replaced by close equivalents. Fullwidth * characters at odd horizontal coordinates will be lost. The operation is * not guaranteed to be reversible at all. * * Note that the width of the canvas is divided by two and becomes the * new height. Height is multiplied by two and becomes the new width. If * the original width is an odd number, the division is rounded up. * * If an error occurs, -1 is returned and \b errno is set accordingly: * - \c EBUSY The canvas is in use by a display driver and cannot be rotated. * - \c ENOMEM Not enough memory to allocate the new canvas size. If this * happens, the previous canvas handle is still valid. * * \param cv The canvas to rotate right. * \return 0 in case of success, -1 if an error occurred. */ int caca_rotate_right(caca_canvas_t *cv) { uint32_t *newchars, *newattrs; int x, y, w2, h2; if(cv->refcount) { seterrno(EBUSY); return -1; } /* Save the current frame shortcuts */ _caca_save_frame_info(cv); w2 = (cv->width + 1) / 2; h2 = cv->height; newchars = malloc(w2 * h2 * 2 * sizeof(uint32_t)); if(!newchars) { seterrno(ENOMEM); return -1; } newattrs = malloc(w2 * h2 * 2 * sizeof(uint32_t)); if(!newattrs) { free(newchars); seterrno(ENOMEM); return -1; } for(y = 0; y < h2; y++) { for(x = 0; x < w2; x++) { uint32_t pair[2], attr1, attr2; pair[0] = cv->chars[cv->width * y + x * 2]; attr1 = cv->attrs[cv->width * y + x * 2]; if((cv->width & 1) && x == w2 - 1) { /* Special case: odd column */ pair[1] = ' '; attr2 = attr1; } else { pair[1] = cv->chars[cv->width * y + x * 2 + 1]; attr2 = cv->attrs[cv->width * y + x * 2 + 1]; } /* If one of the characters is a space, we simply ignore * its colour attributes. Otherwise the resulting characters * may have totally wrong colours. */ if(pair[0] == ' ') attr1 = attr2; else if(pair[1] == ' ') attr2 = attr1; rightpair(pair); newchars[(h2 * x + h2 - 1 - y) * 2] = pair[0]; newattrs[(h2 * x + h2 - 1 - y) * 2] = attr1; newchars[(h2 * x + h2 - 1 - y) * 2 + 1] = pair[1]; newattrs[(h2 * x + h2 - 1 - y) * 2 + 1] = attr2; } } free(cv->chars); free(cv->attrs); /* Swap X and Y information */ x = cv->frames[cv->frame].x; y = cv->frames[cv->frame].y; cv->frames[cv->frame].x = (cv->height - 1 - y) * 2; cv->frames[cv->frame].y = x / 2; x = cv->frames[cv->frame].handlex; y = cv->frames[cv->frame].handley; cv->frames[cv->frame].handlex = (cv->height - 1 - y) * 2; cv->frames[cv->frame].handley = x / 2; cv->frames[cv->frame].width = cv->height * 2; cv->frames[cv->frame].height = (cv->width + 1) / 2; cv->frames[cv->frame].chars = newchars; cv->frames[cv->frame].attrs = newattrs; /* Reset the current frame shortcuts */ _caca_load_frame_info(cv); if(!cv->dirty_disabled) caca_add_dirty_rect(cv, 0, 0, cv->width, cv->height); return 0; }