summaryrefslogtreecommitdiff
path: root/app/xterm/graphics.c
diff options
context:
space:
mode:
Diffstat (limited to 'app/xterm/graphics.c')
-rw-r--r--app/xterm/graphics.c1475
1 files changed, 1475 insertions, 0 deletions
diff --git a/app/xterm/graphics.c b/app/xterm/graphics.c
new file mode 100644
index 000000000..9bdc50c02
--- /dev/null
+++ b/app/xterm/graphics.c
@@ -0,0 +1,1475 @@
+/* $XTermId: graphics.c,v 1.12 2013/07/10 22:35:28 Ross.Combs Exp $ */
+
+/*
+ * Copyright 2013 by Ross Combs
+ *
+ * All Rights Reserved
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Except as contained in this notice, the name(s) of the above copyright
+ * holders shall not be used in advertising or otherwise to promote the
+ * sale, use or other dealings in this Software without prior written
+ * authorization.
+ */
+
+#include <xterm.h>
+
+#include <stdio.h>
+#include <math.h>
+#include <ctype.h>
+#include <stdlib.h>
+
+#include <data.h>
+#include <VTparse.h>
+#include <ptyx.h>
+
+#include <assert.h>
+#include <graphics.h>
+
+#undef DUMP_SIXEL_BITMAP
+#undef DEBUG_REFRESH
+
+/* TODO:
+ * ReGIS:
+ * - everything
+ * sixel:
+ * - erase graphic when erasing screen
+ * - maintain ordered list/array instead of qsort()
+ * - erase text under graphics if bg not transparent
+ * - erase scrolled portions of all graphics on alt buffer
+ * - delete graphic if scrolled past end of scrollback
+ * - delete graphic if all pixels are transparent/erased
+ * - dynamic memory allocation, add configurable limits
+ * - auto convert color graphics in VT330 mode
+ * - investigate second graphic framebuffer for ReGIS -- does this apply to text and sixel graphics?
+ * - fix problem where new_row < 0 during sixel parsing (see FIXME)
+ * VT55/VT105 waveform graphics
+ * - everything
+ * escape sequences
+ * - way to query font size without "window ops" (or make "window ops" permissions more fine grained)
+ * - way to query and/or set the maximum number of color registers
+ */
+
+/* font sizes:
+ * VT510:
+ * 80 Columns 132 Columns Maximum Number of Lines
+ * 10 x 16 6 x 16 26 lines + keyboard indicator line
+ * 10 x 10 6 x 10 42 lines + keyboard indicator line
+ * 10 x 8 6 x 8 53 lines + keyboard indicator line
+ * 10 x 13 6 x 13 26 lines + keyboard indicator line
+*/
+
+/***====================================================================***/
+/*
+ * Parse numeric parameters which have the operator as a prefix rather than a
+ * suffix as in ANSI format.
+ *
+ * # 0
+ * #1 1
+ * #1; 1
+ * "1;2;640;480 4
+ * #1;2;0;0;0 5
+ */
+static void
+parse_prefixedtype_params(ANSI *params, const char **string)
+{
+ const char *cp = *string;
+ ParmType nparam = 0;
+ int last_empty = 1;
+
+ memset(params, 0, sizeof(*params));
+ params->a_final = CharOf(*cp);
+ if (*cp != '\0')
+ cp++;
+
+ while (*cp != '\0') {
+ Char ch = CharOf(*cp);
+
+ if (isdigit(ch)) {
+ last_empty = 0;
+ if (nparam < NPARAM) {
+ params->a_param[nparam] =
+ (ParmType) ((params->a_param[nparam] * 10)
+ + (ch - '0'));
+ }
+ } else if (ch == ';') {
+ last_empty = 1;
+ nparam++;
+ } else if (ch == ' ' || ch == '\r' || ch == '\n') {
+ /* EMPTY */ ;
+ } else {
+ break;
+ }
+ cp++;
+ }
+
+ *string = cp;
+ if (!last_empty)
+ nparam++;
+ if (nparam > NPARAM)
+ params->a_nparam = NPARAM;
+ else
+ params->a_nparam = nparam;
+}
+
+typedef struct {
+ Pixel pix;
+ short r, g, b;
+ short allocated;
+} ColorRegister;
+
+#define MAX_COLOR_REGISTERS 256U
+#define COLOR_HOLE ((unsigned short)MAX_COLOR_REGISTERS)
+#define BUFFER_WIDTH 1000
+#define BUFFER_HEIGHT 800
+typedef struct {
+ RegisterNum pixels[BUFFER_HEIGHT * BUFFER_WIDTH];
+ ColorRegister private_color_registers[MAX_COLOR_REGISTERS];
+ ColorRegister *color_registers;
+ char color_registers_used[MAX_COLOR_REGISTERS];
+ XtermWidget xw;
+ int max_width; /* largest image which can be stored */
+ int max_height; /* largest image which can be stored */
+ RegisterNum current_register;
+ int valid_registers; /* for wrap-around behavior */
+ int device_background; /* 0: set to color 0, 1: unchanged */
+ int background; /* current background color */
+ int aspect_vertical;
+ int aspect_horizontal;
+ int declared_width; /* size as reported by the application */
+ int declared_height; /* size as reported by the application */
+ int actual_width; /* size measured during parsing */
+ int actual_height; /* size measured during parsing */
+ int private_colors; /* if not using the shared color registers */
+ int charrow; /* upper left starting point in characters */
+ int charcol; /* upper left starting point in characters */
+ int pixw; /* width of graphic pixels in screen pixels */
+ int pixh; /* height of graphic pixels in screen pixels */
+ int row; /* context used during parsing */
+ int col; /* context used during parsing */
+ int bufferid; /* which screen buffer the graphic is associated with */
+ unsigned int id; /* sequential id used for preserving layering */
+ int valid; /* if the graphic has been initialized */
+ int dirty; /* if the graphic needs to be redrawn */
+} SixelGraphic;
+
+static unsigned int next_sixel_id = 0U;
+
+static ColorRegister shared_color_registers[MAX_COLOR_REGISTERS];
+
+#define MAX_SIXEL_GRAPHICS 16U
+static SixelGraphic sixel_graphics[MAX_SIXEL_GRAPHICS];
+
+/* sixel scrolling:
+ * VK100/GIGI ? (did it even support Sixel?)
+ * VT125 unsupported
+ * VT240 unsupported
+ * VT241 unsupported
+ * VT330 mode setting
+ * VT340 mode setting
+ * dxterm ?
+ */
+
+static void
+init_sixel_background(SixelGraphic *graphic)
+{
+ RegisterNum bgcolor = (RegisterNum) graphic->background;
+ int r, c;
+
+ TRACE(("initializing sixel background to size=%dx%d bgcolor=%hu\n",
+ graphic->declared_width,
+ graphic->declared_height,
+ bgcolor));
+ for (r = 0; r < graphic->max_height; r++) {
+ for (c = 0; c < graphic->max_width; c++) {
+ if (c < graphic->declared_width && r < graphic->declared_height) {
+ graphic->pixels[r * graphic->max_width + c] = bgcolor;
+ } else {
+ graphic->pixels[r * graphic->max_width + c] = COLOR_HOLE;
+ }
+ }
+ }
+}
+
+static void
+set_sixel(SixelGraphic *graphic, int sixel)
+{
+ RegisterNum color;
+ int pix;
+
+ color = graphic->current_register;
+ TRACE(("drawing sixel at pos=%d,%d color=%hu (hole=%d, [%d,%d,%d])\n",
+ graphic->col,
+ graphic->row,
+ color,
+ color == COLOR_HOLE,
+ ((color != COLOR_HOLE)
+ ? (unsigned int) graphic->color_registers[color].r : 0U),
+ ((color != COLOR_HOLE)
+ ? (unsigned int) graphic->color_registers[color].g : 0U),
+ ((color != COLOR_HOLE)
+ ? (unsigned int) graphic->color_registers[color].b : 0U)));
+ for (pix = 0; pix < 6; pix++) {
+ if (graphic->col < graphic->max_width &&
+ graphic->row + pix < graphic->max_height) {
+ if (sixel & (1 << pix)) {
+ if (graphic->col + 1 > graphic->actual_width) {
+ graphic->actual_width = graphic->col + 1;
+ }
+ if (graphic->row + pix + 1 > graphic->actual_height) {
+ graphic->actual_height = graphic->row + pix + 1;
+ }
+ graphic->pixels[
+ (((graphic->row + pix) * graphic->max_width)
+ + graphic->col)
+ ] = color;
+ }
+ } else {
+ TRACE(("sixel pixel %d out of bounds\n", pix));
+ }
+ }
+}
+
+static void
+set_sixel_color_register(ColorRegister *color_registers,
+ RegisterNum color,
+ short r,
+ short g,
+ short b)
+{
+ ColorRegister *reg = &color_registers[color];
+ reg->r = r;
+ reg->g = g;
+ reg->b = b;
+ reg->allocated = 0;
+}
+
+static void
+init_color_registers(ColorRegister *color_registers, int terminal_id)
+{
+ TRACE(("initializing colors for %d\n", terminal_id));
+ {
+ unsigned int i;
+
+ for (i = 0U; i < MAX_COLOR_REGISTERS; i++) {
+ set_sixel_color_register(color_registers, (RegisterNum) i, 0, 0, 0);
+ }
+ }
+
+ /*
+ * default color registers:
+ * (mono) (color)
+ * VK100/GIGI (fixed)
+ * VT125:
+ * 0: 0% 0%
+ * 1: 33% blue
+ * 2: 66% red
+ * 3: 100% green
+ * VT240:
+ * 0: 0% 0%
+ * 1: 33% blue
+ * 2: 66% red
+ * 3: 100% green
+ * VT241:
+ * 0: 0% 0%
+ * 1: 33% blue
+ * 2: 66% red
+ * 3: 100% green
+ * VT330:
+ * 0: 0% 0% (bg for light on dark mode)
+ * 1: 33% blue (red?)
+ * 2: 66% red (green?)
+ * 3: 100% green (yellow?) (fg for light on dark mode)
+ * VT340:
+ * 0: 0% 0% (bg for light on dark mode)
+ * 1: 14% blue
+ * 2: 29% red
+ * 3: 43% green
+ * 4: 57% magenta
+ * 5: 71% cyan
+ * 6: 86% yellow
+ * 7: 100% 50% (fg for light on dark mode)
+ * 8: 0% 25%
+ * 9: 14% gray-blue
+ * 10: 29% gray-red
+ * 11: 43% gray-green
+ * 12: 57% gray-magenta
+ * 13: 71% gray-cyan
+ * 14: 86% gray-yellow
+ * 15: 100% 75%
+ * dxterm:
+ * ?
+ */
+ switch (terminal_id) {
+ case 125:
+ case 241:
+ set_sixel_color_register(color_registers, 0, 0, 0, 0);
+ set_sixel_color_register(color_registers, 1, 0, 0, 100);
+ set_sixel_color_register(color_registers, 2, 0, 100, 0);
+ set_sixel_color_register(color_registers, 3, 100, 0, 0);
+ break;
+ case 240:
+ case 330:
+ set_sixel_color_register(color_registers, 0, 0, 0, 0);
+ set_sixel_color_register(color_registers, 1, 33, 33, 33);
+ set_sixel_color_register(color_registers, 2, 66, 66, 66);
+ set_sixel_color_register(color_registers, 3, 100, 100, 100);
+ break;
+ case 340:
+ default:
+ set_sixel_color_register(color_registers, 0, 0, 0, 0);
+ set_sixel_color_register(color_registers, 1, 20, 20, 80);
+ set_sixel_color_register(color_registers, 2, 80, 13, 13);
+ set_sixel_color_register(color_registers, 3, 20, 80, 20);
+ set_sixel_color_register(color_registers, 4, 80, 20, 80);
+ set_sixel_color_register(color_registers, 5, 20, 80, 80);
+ set_sixel_color_register(color_registers, 6, 80, 80, 20);
+ set_sixel_color_register(color_registers, 7, 53, 53, 53);
+ set_sixel_color_register(color_registers, 8, 26, 26, 26);
+ set_sixel_color_register(color_registers, 9, 33, 33, 60);
+ set_sixel_color_register(color_registers, 10, 60, 26, 26);
+ set_sixel_color_register(color_registers, 11, 33, 60, 33);
+ set_sixel_color_register(color_registers, 12, 60, 33, 60);
+ set_sixel_color_register(color_registers, 13, 33, 60, 60);
+ set_sixel_color_register(color_registers, 14, 60, 60, 33);
+ set_sixel_color_register(color_registers, 15, 80, 80, 80);
+ break;
+ }
+}
+
+static void
+init_sixel_graphic(SixelGraphic *graphic, int terminal_id, int private_colors)
+{
+ TRACE(("initializing sixel graphic\n"));
+
+ graphic->dirty = 1;
+ memset(graphic->pixels, 0, sizeof(graphic->pixels));
+ memset(graphic->color_registers_used, 0, sizeof(graphic->color_registers_used));
+
+ /*
+ * dimensions (REGIS logical, physical):
+ * VK100/GIGI 768x4?? 768x2??
+ * VT125 768x460 768x230(+10status) (1:2 aspect ratio, REGIS halves vertical addresses through "odd y emulation")
+ * VT240 800x460 800x230(+10status) (1:2 aspect ratio, REGIS halves vertical addresses through "odd y emulation")
+ * VT241 800x460 800x230(+10status) (1:2 aspect ratio, REGIS halves vertical addresses through "odd y emulation")
+ * VT330 800x480 800x480(+?status)
+ * VT340 800x480 800x480(+?status)
+ * dxterm ?x? variable?
+ */
+ graphic->max_width = BUFFER_WIDTH;
+ graphic->max_height = BUFFER_HEIGHT;
+
+ /* default isn't white on the VT240, but not sure what it is */
+ graphic->current_register = 3; /* FIXME: using green, but not sure what it should be */
+
+ /*
+ * When an application selects the monochrome map: the terminal sets the
+ * 16 entries of the color map to the default monochrome gray level.
+ * Therefore, the original colors are lost when changing from the color map
+ * to the monochrome map.
+ *
+ * If you change the color value (green, red, blue) using the Color Set-Up
+ * screen or a ReGIS command, the VT340 sets the gray scale by using the
+ * formula (2G + R)/3.
+ *
+ * When an application selects the color map: the terminal sets the 16
+ * entries of the color map to the default (color) color map.
+ */
+
+ /*
+ * color capabilities:
+ * VK100/GIGI 1 plane (12x1 pixel attribute blocks) colorspace is 8 fixed colors (black, white, red, green, blue, cyan, yellow, magenta)
+ * VT125 2 planes (4 registers) colorspace is (64?) (color), ? (grayscale)
+ * VT240 2 planes (4 registers) colorspace is ? shades (grayscale)
+ * VT241 2 planes (4 registers) colorspace is ? (color), ? shades (grayscale)
+ * VT330 2 planes (4 registers) colorspace is 4 shades (grayscale)
+ * VT340 4 planes (16 registers) colorspace is r16g16b16 (color), 16 shades (grayscale)
+ * dxterm ?
+ */
+ switch (terminal_id) {
+ case 125:
+ graphic->valid_registers = 4;
+ break;
+ case 240:
+ graphic->valid_registers = 4;
+ break;
+ case 241:
+ graphic->valid_registers = 4;
+ break;
+ case 330:
+ graphic->valid_registers = 4;
+ break;
+ case 340:
+ graphic->valid_registers = 16;
+ break;
+ default:
+ graphic->valid_registers = 64; /* unknown graphics model -- might as well be generous */
+ break;
+ }
+
+ /*
+ * text and graphics interactions:
+ * VK100/GIGI text writes on top of graphics buffer, color attribute shared with text
+ * VT240,VT241,VT330,VT340 text writes on top of graphics buffer
+ * VT125 graphics buffer overlaid on top of text in B&W display, text not present in color display
+ */
+
+ /* FIXME: is this always zero? what about in light background mode? */
+ graphic->device_background = 0; /* default background color register */
+
+ /* pixel sizes seem to have differed by model and options */
+ /* VT240 and VT340 defaulted to 2:1 ratio */
+ graphic->aspect_vertical = 2;
+ graphic->aspect_horizontal = 1;
+
+ graphic->declared_width = 0;
+ graphic->declared_height = 0;
+
+ graphic->actual_width = 0;
+ graphic->actual_height = 0;
+
+ graphic->private_colors = private_colors;
+ if (graphic->private_colors) {
+ TRACE(("sixel using private color registers\n"));
+ init_color_registers(graphic->private_color_registers, terminal_id);
+ graphic->color_registers = graphic->private_color_registers;
+ } else {
+ TRACE(("sixel using shared color registers\n"));
+ graphic->color_registers = shared_color_registers;
+ }
+
+ graphic->charrow = 0;
+ graphic->charcol = 0;
+
+ graphic->row = 0;
+ graphic->col = 0;
+
+ graphic->valid = 0;
+}
+
+static SixelGraphic *
+get_sixel_graphic(XtermWidget xw)
+{
+ TScreen const *screen = TScreenOf(xw);
+ int bufferid = screen->whichBuf;
+ int terminal_id = screen->terminal_id;
+ int private_colors = screen->privatecolorregisters;
+ SixelGraphic *graphic;
+ unsigned int ii;
+
+ for (ii = 0U; ii < MAX_SIXEL_GRAPHICS; ii++) {
+ graphic = &sixel_graphics[ii];
+ if (!graphic->valid)
+ break;
+ }
+
+ if (ii >= MAX_SIXEL_GRAPHICS) {
+ int min_charrow = 0;
+ SixelGraphic *min_graphic = NULL;
+
+ for (ii = 0U; ii < MAX_SIXEL_GRAPHICS; ii++) {
+ graphic = &sixel_graphics[ii];
+ if (!min_graphic || graphic->charrow < min_charrow) {
+ min_charrow = graphic->charrow;
+ min_graphic = graphic;
+ }
+ }
+ graphic = min_graphic;
+ }
+
+ graphic->xw = xw;
+ graphic->bufferid = bufferid;
+ graphic->id = next_sixel_id++;
+ init_sixel_graphic(graphic, terminal_id, private_colors);
+ return graphic;
+}
+
+static void
+dump_sixel(SixelGraphic const *graphic)
+{
+#ifdef DUMP_SIXEL_BITMAP
+ int r, c;
+ RegisterNum color;
+ ColorRegister const *reg;
+#endif
+
+ (void) graphic;
+
+ TRACE(("sixel stats: charrow=%d charcol=%d actual_width=%d actual_height=%d pixw=%d pixh=%d\n",
+ graphic->charrow,
+ graphic->charcol,
+ graphic->actual_width,
+ graphic->actual_height,
+ graphic->pixw,
+ graphic->pixh));
+
+#ifdef DUMP_SIXEL_BITMAP
+ TRACE(("sixel dump:\n"));
+ for (r = 0; r < graphic->max_height; r++) {
+ for (c = 0; c < graphic->max_width; c++) {
+ color = graphic->pixels[r * graphic->max_width + c];
+ if (color == COLOR_HOLE) {
+ TRACE(("?"));
+ } else {
+ reg = &graphic->color_registers[color];
+ if (reg->r + reg->g + reg->b > 200) {
+ TRACE(("#"));
+ } else if (reg->r + reg->g + reg->b > 150) {
+ TRACE(("%%"));
+ } else if (reg->r + reg->g + reg->b > 100) {
+ TRACE((":"));
+ } else if (reg->r + reg->g + reg->b > 80) {
+ TRACE(("."));
+ } else {
+ TRACE((" "));
+ }
+ }
+ }
+ TRACE(("\n"));
+ }
+#endif
+ TRACE(("\n"));
+}
+
+static void
+set_shared_color_register(RegisterNum color, short r, short g, short b)
+{
+ SixelGraphic *graphic;
+ unsigned int ii;
+
+ assert(color < MAX_COLOR_REGISTERS);
+
+ set_sixel_color_register(shared_color_registers, color, r, g, b);
+
+ for (ii = 0U; ii < MAX_SIXEL_GRAPHICS; ii++) {
+ graphic = &sixel_graphics[ii];
+ if (graphic->private_colors)
+ continue;
+
+ if (graphic->color_registers_used[ii]) {
+ graphic->dirty = 1;
+ }
+ }
+}
+
+#define ScaleForXColor(s) (unsigned short) ((long)(s) * 65535 / 100)
+
+static Pixel
+sixel_register_to_xpixel(ColorRegister *reg, XtermWidget xw)
+{
+ if (!reg->allocated) {
+ XColor def;
+
+ def.red = ScaleForXColor(reg->r);
+ def.green = ScaleForXColor(reg->g);
+ def.blue = ScaleForXColor(reg->b);
+ def.flags = DoRed | DoGreen | DoBlue;
+ if (!allocateBestRGB(xw, &def)) {
+ return 0UL;
+ }
+ reg->pix = def.pixel;
+ reg->allocated = 1;
+ }
+
+ /* FIXME: with so many possible colors we need to determine
+ * when to free them to be nice to PseudoColor displays
+ */
+ return reg->pix;
+}
+
+static void
+refresh_sixel_graphic(TScreen const *screen,
+ SixelGraphic *graphic,
+ int xbase,
+ int ybase,
+ int x,
+ int y,
+ int w,
+ int h)
+{
+ Display *display = screen->display;
+ Window vwindow = WhichVWin(screen)->window;
+ GC graphics_gc;
+ int r, c;
+ int wx, wy;
+ int pw, ph;
+ RegisterNum color;
+ RegisterNum old_fg;
+ XGCValues xgcv;
+ XtGCMask mask;
+ int holes, total;
+
+ TRACE(("refreshing sixel graphic from %d,%d %dx%d (valid=%d, bg=%dx%d size=%dx%d, max=%dx%d) at base=%d,%d\n",
+ x, y, w, h,
+ graphic->valid,
+ graphic->declared_width,
+ graphic->declared_height,
+ graphic->actual_width,
+ graphic->actual_height,
+ graphic->max_width,
+ graphic->max_height,
+ xbase, ybase));
+
+ memset(&xgcv, 0, sizeof(xgcv));
+ xgcv.foreground = 0UL;
+ xgcv.graphics_exposures = False;
+ mask = GCForeground | GCGraphicsExposures;
+ graphics_gc = XCreateGC(display, vwindow, mask, &xgcv);
+
+ pw = graphic->pixw;
+ ph = graphic->pixh;
+
+ TRACE(("refreshed graphic covers 0,0 to %d,%d\n",
+ (graphic->actual_width - 1) * pw + pw - 1,
+ (graphic->actual_height - 1) * ph + ph - 1));
+ TRACE(("refreshed area covers %d,%d to %d,%d\n",
+ x, y,
+ x + w - 1,
+ y + h - 1));
+
+ old_fg = COLOR_HOLE;
+ holes = total = 0;
+ for (r = 0; r < graphic->actual_height; r++)
+ for (c = 0; c < graphic->actual_width; c++) {
+ if (r * ph + ph - 1 < y ||
+ r * ph > y + h - 1 ||
+ c * pw + pw - 1 < x ||
+ c * pw > x + w - 1)
+ continue;
+
+ wy = ybase + r * ph;
+ wx = xbase + c * pw;
+
+ total++;
+ color = graphic->pixels[r * graphic->max_width + c];
+ if (color == COLOR_HOLE) {
+ holes++;
+ continue;
+ }
+
+ if (color != old_fg) {
+ xgcv.foreground =
+ sixel_register_to_xpixel(&graphic->color_registers[color],
+ graphic->xw);
+ XChangeGC(display, graphics_gc, mask, &xgcv);
+ old_fg = color;
+ }
+
+ XFillRectangle(display, vwindow, graphics_gc,
+ wx, wy, (unsigned) pw, (unsigned) ph);
+ }
+
+#ifdef DEBUG_REFRESH
+ {
+ XColor def;
+
+ def.red = (short) (1.0 * 65535.0);
+ def.green = (short) (0.1 * 65535.0);
+ def.blue = (short) (1.0 * 65535.0);
+ def.flags = DoRed | DoGreen | DoBlue;
+ if (allocateBestRGB(graphic->xw, &def)) {
+ xgcv.foreground = def.pixel;
+ XChangeGC(display, graphics_gc, mask, &xgcv);
+ }
+ XFillRectangle(display, vwindow, graphics_gc,
+ xbase + 0,
+ ybase + 0,
+ (unsigned) pw, (unsigned) ph);
+ XFillRectangle(display, vwindow, graphics_gc,
+ xbase + (graphic->actual_width - 1) * pw,
+ ybase + (graphic->actual_height - 1) * ph,
+ (unsigned) pw, (unsigned) ph);
+
+ def.red = (unsigned short) ((1.0 - 0.1 * (rand() / (double)
+ RAND_MAX) * 65535.0));
+ def.green = (unsigned short) ((0.7 + 0.2 * (rand() / (double)
+ RAND_MAX)) * 65535.0);
+ def.blue = (unsigned short) ((0.1 + 0.1 * (rand() / (double)
+ RAND_MAX)) * 65535.0);
+ def.flags = DoRed | DoGreen | DoBlue;
+ if (allocateBestRGB(graphic->xw, &def)) {
+ xgcv.foreground = def.pixel;
+ XChangeGC(display, graphics_gc, mask, &xgcv);
+ }
+ XDrawLine(display, vwindow, graphics_gc,
+ xbase + x + 0, ybase + y + 0,
+ xbase + x + w - 1, ybase + y + 0);
+ XDrawLine(display, vwindow, graphics_gc,
+ xbase + x + w - 1, ybase + y + 0,
+ xbase + x + 0, ybase + y + h - 1);
+ XDrawLine(display, vwindow, graphics_gc,
+ xbase + x + 0, ybase + y + h - 1,
+ xbase + x + w - 1, ybase + y + h - 1);
+ XDrawLine(display, vwindow, graphics_gc,
+ xbase + x + w - 1, ybase + y + h - 1,
+ xbase + x + 0, ybase + y + 0);
+ }
+#endif
+ XFlush(display);
+ TRACE(("done refreshing sixel graphic: %d of %d refreshed pixels were holes\n",
+ holes, total));
+
+ XFreeGC(display, graphics_gc);
+}
+
+/*
+ * Primary color hues:
+ * blue: 0 degrees
+ * red: 120 degrees
+ * green: 240 degrees
+ */
+static void
+hls2rgb(int h, int l, int s, short *r, short *g, short *b)
+{
+ double hs = (h + 240) % 360;
+ double hv = hs / 360.0;
+ double lv = l / 100.0;
+ double sv = s / 100.0;
+ double c, x, m;
+ double r1, g1, b1;
+ int hpi;
+
+ if (s == 0) {
+ *r = *g = *b = (short) l;
+ return;
+ }
+
+ c = (1.0 - fabs(2.0 * lv - 1.0)) * sv;
+ hpi = (int) (hv * 6.0);
+ x = (hpi & 1) ? c : 0.0;
+ m = lv - 0.5 * c;
+
+ switch (hpi) {
+ case 0:
+ r1 = c;
+ g1 = x;
+ b1 = 0.0;
+ break;
+ case 1:
+ r1 = x;
+ g1 = c;
+ b1 = 0.0;
+ break;
+ case 2:
+ r1 = 0.0;
+ g1 = c;
+ b1 = x;
+ break;
+ case 3:
+ r1 = 0.0;
+ g1 = x;
+ b1 = c;
+ break;
+ case 4:
+ r1 = x;
+ g1 = 0.0;
+ b1 = c;
+ break;
+ case 5:
+ r1 = c;
+ g1 = 0.0;
+ b1 = x;
+ break;
+ default:
+ printf("BAD\n");
+ *r = (short) 100;
+ *g = (short) 100;
+ *b = (short) 100;
+ return;
+ }
+
+ *r = (short) ((r1 + m) * 100.0 + 0.5);
+ *g = (short) ((g1 + m) * 100.0 + 0.5);
+ *b = (short) ((b1 + m) * 100.0 + 0.5);
+
+ if (*r < 0)
+ *r = 0;
+ else if (*r > 100)
+ *r = 100;
+ if (*g < 0)
+ *g = 0;
+ else if (*g > 100)
+ *g = 100;
+ if (*b < 0)
+ *b = 0;
+ else if (*b > 100)
+ *b = 100;
+}
+
+static void
+update_sixel_aspect(SixelGraphic *graphic)
+{
+ /* We want to keep the ratio accurate but would like every pixel to have
+ * the same size so keep these as whole numbers.
+ */
+ /* FIXME: DEC terminals had pixels about twice as tall as they were wide,
+ * and it seems the VT125 and VT24x only used data from odd graphic rows.
+ * This means it basically cancels out if we ignore both, except that
+ * the even rows of pixels may not be written by the application such that
+ * they are suitable for display. In practice this doesn't seem to be
+ * an issue but I have very few test files/programs.
+ */
+ if (graphic->aspect_vertical < graphic->aspect_horizontal) {
+ graphic->pixw = 1;
+ graphic->pixh = ((graphic->aspect_vertical
+ + graphic->aspect_horizontal - 1)
+ / graphic->aspect_horizontal);
+ } else {
+ graphic->pixw = ((graphic->aspect_horizontal
+ + graphic->aspect_vertical - 1)
+ / graphic->aspect_vertical);
+ graphic->pixh = 1;
+ }
+ TRACE(("sixel aspect ratio: an=%d ad=%d -> pixw=%d pixh=%d\n",
+ graphic->aspect_vertical,
+ graphic->aspect_horizontal,
+ graphic->pixw,
+ graphic->pixh));
+}
+
+/*
+ * Interpret sixel graphics sequences.
+ *
+ * Resources:
+ * http://en.wikipedia.org/wiki/Sixel
+ * http://vt100.net/docs/vt3xx-gp/chapter14.html
+ * ftp://ftp.cs.utk.edu/pub/shuford/terminal/sixel_graphics_news.txt
+ * ftp://ftp.cs.utk.edu/pub/shuford/terminal/all_about_sixels.txt
+ */
+extern void
+parse_sixel(XtermWidget xw, ANSI *params, char const *string)
+{
+ TScreen *screen = TScreenOf(xw);
+ SixelGraphic *graphic;
+ Char ch;
+
+ graphic = get_sixel_graphic(xw);
+
+ {
+ int Pmacro = params->a_param[0];
+ int Pbgmode = params->a_param[1];
+ int Phgrid = params->a_param[2];
+ int Pan = params->a_param[3];
+ int Pad = params->a_param[4];
+ int Ph = params->a_param[5];
+ int Pv = params->a_param[6];
+
+ (void) Phgrid;
+
+ TRACE(("sixel bitmap graphics sequence: params=%d (Pmacro=%d Pbgmode=%d Phgrid=%d) scroll_amt=%d\n",
+ params->a_nparam,
+ Pmacro,
+ Pbgmode,
+ Phgrid,
+ screen->scroll_amt));
+
+ switch (params->a_nparam) {
+ case 7:
+ if (Pan == 0 || Pad == 0) {
+ TRACE(("DATA_ERROR: invalid raster ratio %d/%d\n", Pan, Pad));
+ return;
+ }
+ graphic->aspect_vertical = Pan;
+ graphic->aspect_horizontal = Pad;
+
+ if (Ph == 0 || Pv == 0) {
+ TRACE(("DATA_ERROR: raster image dimensions are invalid %dx%d\n",
+ Ph, Pv));
+ return;
+ }
+ if (Ph > graphic->max_width || Pv > graphic->max_height) {
+ TRACE(("DATA_ERROR: raster image dimensions are too large %dx%d\n",
+ Ph, Pv));
+ return;
+ }
+ graphic->declared_width = Ph;
+ graphic->declared_height = Pv;
+ if (graphic->declared_width > graphic->actual_width) {
+ graphic->actual_width = graphic->declared_width;
+ }
+ if (graphic->declared_height > graphic->actual_height) {
+ graphic->actual_height = graphic->declared_height;
+ }
+ break;
+ case 3:
+ case 2:
+ switch (Pmacro) {
+ case 0:
+ case 1:
+ graphic->aspect_vertical = 5;
+ graphic->aspect_horizontal = 1;
+ break;
+ case 2:
+ graphic->aspect_vertical = 3;
+ graphic->aspect_horizontal = 1;
+ break;
+ case 3:
+ case 4:
+ graphic->aspect_vertical = 2;
+ graphic->aspect_horizontal = 1;
+ break;
+ case 5:
+ case 6:
+ graphic->aspect_vertical = 2;
+ graphic->aspect_horizontal = 1;
+ break;
+ case 7:
+ case 8:
+ case 9:
+ graphic->aspect_vertical = 1;
+ graphic->aspect_horizontal = 1;
+ break;
+ default:
+ TRACE(("DATA_ERROR: unknown sixel macro mode parameter\n"));
+ return;
+ }
+ break;
+ case 0:
+ break;
+ default:
+ TRACE(("DATA_ERROR: unexpected parameter count (found %d)\n", params->a_nparam));
+ return;
+ }
+
+ if (Pbgmode == 1) {
+ graphic->background = COLOR_HOLE;
+ } else {
+ graphic->background = graphic->device_background;
+ }
+
+ /* Ignore the grid parameter because it seems only printers paid attention to it.
+ * The VT3xx was always 0.0195 cm.
+ */
+ }
+
+#if OPT_SIXEL_GRAPHICS
+ if (xw->keyboard.flags & MODE_DECSDM) {
+ TRACE(("sixel scrolling enabled: inline positioning for graphic\n"));
+ graphic->charrow = screen->cur_row;
+ graphic->charcol = screen->cur_col;
+ }
+
+ update_sixel_aspect(graphic);
+#endif
+
+ for (;;) {
+ ch = CharOf(*string);
+ if (ch == '\0')
+ break;
+
+ if (ch >= 0x3f && ch <= 0x7e) {
+ int sixel = ch - 0x3f;
+ TRACE(("sixel=%x (%c)\n", sixel, (char) ch));
+ if (!graphic->valid) {
+ init_sixel_background(graphic);
+ graphic->valid = 1;
+ }
+ set_sixel(graphic, sixel);
+ graphic->col++;
+ } else if (ch == '$') { /* DECGCR */
+ /* ignore DECCRNLM in sixel mode */
+ TRACE(("sixel CR\n"));
+ graphic->col = 0;
+ } else if (ch == '-') { /* DECGNL */
+ int scroll_lines;
+ TRACE(("sixel NL\n"));
+ scroll_lines = 0;
+ while (graphic->charrow - scroll_lines +
+ (((graphic->row + 6) * graphic->pixh
+ + FontHeight(screen) - 1)
+ / FontHeight(screen)) > screen->bot_marg) {
+ scroll_lines++;
+ }
+ graphic->col = 0;
+ graphic->row += 6;
+ /* If we hit the bottom margin on the graphics page (well, we just use the text margin for now),
+ * the behavior is to either scroll or to discard the remainder of the graphic depending on this
+ * setting.
+ */
+ if (scroll_lines > 0) {
+ if (xw->keyboard.flags & MODE_DECSDM) {
+ Display *display = screen->display;
+ xtermScroll(xw, scroll_lines);
+ XSync(display, False);
+ TRACE(("graphic scrolled the screen %d lines. screen->scroll_amt=%d screen->topline=%d, now starting row is %d\n",
+ scroll_lines,
+ screen->scroll_amt,
+ screen->topline,
+ graphic->charrow));
+ } else {
+ break;
+ }
+ }
+ } else if (ch == '!') { /* DECGRI */
+ int Pcount;
+ const char *start;
+ int sixel;
+ int i;
+
+ start = ++string;
+ for (;;) {
+ ch = CharOf(*string);
+ if (ch != '0' &&
+ ch != '1' &&
+ ch != '2' &&
+ ch != '3' &&
+ ch != '4' &&
+ ch != '5' &&
+ ch != '6' &&
+ ch != '7' &&
+ ch != '8' &&
+ ch != '9' &&
+ ch != ' ' &&
+ ch != '\r' &&
+ ch != '\n')
+ break;
+ string++;
+ }
+ if (ch == '\0') {
+ TRACE(("DATA_ERROR: sixel data string terminated in the middle of a repeat operator\n"));
+ return;
+ }
+ if (string == start) {
+ TRACE(("DATA_ERROR: sixel data string contains a repeat operator with empty count\n"));
+ return;
+ }
+ Pcount = atoi(start);
+ sixel = ch - 0x3f;
+ TRACE(("sixel repeat operator: sixel=%d (%c), count=%d\n",
+ sixel, (char) ch, Pcount));
+ if (!graphic->valid) {
+ init_sixel_background(graphic);
+ graphic->valid = 1;
+ }
+ for (i = 0; i < Pcount; i++) {
+ set_sixel(graphic, sixel);
+ graphic->col++;
+ }
+ } else if (ch == '#') { /* DECGCI */
+ ANSI color_params;
+ int Pregister;
+
+ parse_prefixedtype_params(&color_params, &string);
+ Pregister = color_params.a_param[0];
+ if (Pregister >= graphic->valid_registers) {
+ TRACE(("DATA_WARNING: sixel color operator uses out-of-range register %d\n", Pregister));
+ /* FIXME: supposedly the DEC terminals wrapped register indicies -- verify */
+ while (Pregister >= graphic->valid_registers)
+ Pregister -= graphic->valid_registers;
+ TRACE(("DATA_WARNING: converted to %d\n", Pregister));
+ }
+
+ if (color_params.a_nparam > 2 && color_params.a_nparam <= 5) {
+ int Pspace = color_params.a_param[1];
+ int Pc1 = color_params.a_param[2];
+ int Pc2 = color_params.a_param[3];
+ int Pc3 = color_params.a_param[4];
+ short r, g, b;
+
+ TRACE(("sixel set color register=%d space=%d color=[%d,%d,%d] (nparams=%d)\n",
+ Pregister, Pspace, Pc1, Pc2, Pc3, color_params.a_nparam));
+
+ switch (Pspace) {
+ case 1: /* HLS */
+ if (Pc1 > 360 || Pc2 > 100 || Pc3 > 100) {
+ TRACE(("DATA_ERROR: sixel set color operator uses out-of-range HLS color coordinates %d,%d,%d\n",
+ Pc1, Pc2, Pc3));
+ return;
+ }
+ hls2rgb(Pc1, Pc2, Pc3, &r, &g, &b);
+ break;
+ case 2: /* RGB */
+ if (Pc1 > 100 || Pc2 > 100 || Pc3 > 100) {
+ TRACE(("DATA_ERROR: sixel set color operator uses out-of-range RGB color coordinates %d,%d,%d\n",
+ Pc1, Pc2, Pc3));
+ return;
+ }
+ r = (short) Pc1;
+ g = (short) Pc2;
+ b = (short) Pc3;
+ break;
+ default: /* unknown */
+ TRACE(("DATA_ERROR: sixel set color operator uses unknown color space %d\n", Pspace));
+ return;
+ }
+ if (graphic->private_colors) {
+ set_sixel_color_register(graphic->private_color_registers,
+ (RegisterNum) Pregister,
+ r, g, b);
+ } else {
+ set_shared_color_register((RegisterNum) Pregister, r, g, b);
+ }
+ graphic->color_registers_used[Pregister] = 1;
+ } else if (color_params.a_nparam == 1) {
+ TRACE(("sixel switch to color register=%d (nparams=%d)\n",
+ Pregister, color_params.a_nparam));
+ graphic->current_register = (RegisterNum) Pregister;
+ } else {
+ TRACE(("DATA_ERROR: sixel switch color operator with unexpected parameter count (nparams=%d)\n", color_params.a_nparam));
+ return;
+ }
+ continue;
+ } else if (ch == '"') /* DECGRA */ {
+ ANSI raster_params;
+
+ parse_prefixedtype_params(&raster_params, &string);
+ if (raster_params.a_nparam < 2) {
+ TRACE(("DATA_ERROR: sixel raster attribute operator with incomplete parameters (found %d, expected 2 or 4)\n", raster_params.a_nparam));
+ return;
+ } {
+ int Pan = raster_params.a_param[0];
+ int Pad = raster_params.a_param[1];
+ TRACE(("sixel raster attribute with h:w=%d:%d\n", Pan, Pad));
+ if (Pan == 0 || Pad == 0) {
+ TRACE(("DATA_ERROR: invalid raster ratio %d/%d\n", Pan, Pad));
+ return;
+ }
+ graphic->aspect_vertical = Pan;
+ graphic->aspect_horizontal = Pad;
+ update_sixel_aspect(graphic);
+ }
+
+ if (raster_params.a_nparam >= 4) {
+ int Ph = raster_params.a_param[2];
+ int Pv = raster_params.a_param[3];
+
+ TRACE(("sixel raster attribute with h=%d v=%d\n", Ph, Pv));
+ if (Ph == 0 || Pv == 0) {
+ TRACE(("DATA_ERROR: raster image dimensions are invalid %dx%d\n",
+ Ph, Pv));
+ return;
+ }
+ if (Ph > graphic->max_width || Pv > graphic->max_height) {
+ TRACE(("DATA_ERROR: raster image dimensions are too large %dx%d\n",
+ Ph, Pv));
+ return;
+ }
+ graphic->declared_width = Ph;
+ graphic->declared_height = Pv;
+ if (graphic->declared_width > graphic->actual_width) {
+ graphic->actual_width = graphic->declared_width;
+ }
+ if (graphic->declared_height > graphic->actual_height) {
+ graphic->actual_height = graphic->declared_height;
+ }
+ }
+
+ continue;
+ } else if (ch == ' ' || ch == '\r' || ch == '\n') {
+ /* EMPTY */ ;
+ } else {
+ TRACE(("DATA_ERROR: unknown sixel command %04x (%c)\n",
+ (int) ch, ch));
+ }
+
+ string++;
+ }
+
+ /* update the screen */
+ if (screen->scroll_amt)
+ FlushScroll(xw);
+
+ if (xw->keyboard.flags & MODE_DECSDM) {
+ int new_row = (graphic->charrow
+ + ((graphic->actual_height * graphic->pixh)
+ / FontHeight(screen)));
+ int new_col = (graphic->charcol
+ + (((graphic->actual_width * graphic->pixw)
+ + FontWidth(screen) - 1)
+ / FontWidth(screen)));
+
+ TRACE(("setting text position after %dx%d graphic starting on row=%d col=%d: cursor new_row=%d new_col=%d\n",
+ graphic->actual_width * graphic->pixw,
+ graphic->actual_height * graphic->pixh,
+ graphic->charrow,
+ graphic->charcol,
+ new_row, new_col));
+
+ if (new_col > screen->rgt_marg) {
+ new_col = screen->lft_marg;
+ new_row++;
+ TRACE(("column past left margin, overriding to row=%d col=%d\n",
+ new_row, new_col));
+ }
+
+ while (new_row > screen->bot_marg) {
+ xtermScroll(xw, 1);
+ new_row--;
+ TRACE(("bottom row was past screen. new start row=%d, cursor row=%d\n",
+ graphic->charrow, new_row));
+ }
+
+ if (new_row < 0) {
+ TRACE(("new row is going to be negative (%d)!", new_row)); /* FIXME: this was triggering, now it isn't */
+ }
+ set_cur_row(screen, new_row);
+ set_cur_col(screen, new_col <= screen->rgt_marg ? new_col : screen->rgt_marg);
+ }
+
+ refresh_modified_displayed_graphics(screen);
+
+ TRACE(("DONE successfully parsed sixel data\n"));
+ dump_sixel(graphic);
+}
+
+extern void
+parse_regis(XtermWidget xw, ANSI *params, char const *string)
+{
+ (void) xw;
+ (void) string;
+ (void) params;
+
+ TRACE(("ReGIS vector graphics mode, params=%d\n", params->a_nparam));
+}
+
+/* Erase the portion of any displayed graphic overlapping with a rectangle
+ * of the given size and location in pixels.
+ * This is used to allow text to "erase" graphics underneath it.
+ */
+static void
+erase_sixel_graphic(SixelGraphic *graphic, int x, int y, int w, int h)
+{
+ RegisterNum hole = COLOR_HOLE;
+ int pw, ph;
+ int r, c;
+
+ pw = graphic->pixw;
+ ph = graphic->pixh;
+
+ TRACE(("erasing sixel bitmap %d,%d %dx%d\n", x, y, w, h));
+
+ for (r = 0; r < graphic->actual_height; r++) {
+ for (c = 0; c < graphic->actual_width; c++) {
+ if (r * ph + ph - 1 < y ||
+ r * ph > y + h - 1 ||
+ c * pw + pw - 1 < x ||
+ c * pw > x + w - 1)
+ continue;
+
+ graphic->pixels[r * graphic->max_width + c] = hole;
+ }
+ }
+}
+
+static int
+compare_sixel_ids(const void *left, const void *right)
+{
+ const SixelGraphic *l = *(const SixelGraphic *const *) left;
+ const SixelGraphic *r = *(const SixelGraphic *const *) right;
+
+ if (!l->valid || !r->valid)
+ return 0;
+ if (l->id < r->id)
+ return -1;
+ else
+ return 1;
+}
+
+extern void
+refresh_displayed_graphics(TScreen const *screen,
+ int leftcol,
+ int toprow,
+ int ncols,
+ int nrows)
+{
+ SixelGraphic *ordered_graphics[MAX_SIXEL_GRAPHICS];
+ SixelGraphic *graphic;
+ unsigned int ii;
+ int x, y, w, h;
+ int xbase, ybase;
+
+ for (ii = 0U; ii < MAX_SIXEL_GRAPHICS; ii++) {
+ ordered_graphics[ii] = &sixel_graphics[ii];
+ }
+ qsort(ordered_graphics,
+ MAX_SIXEL_GRAPHICS,
+ sizeof(ordered_graphics[0]),
+ compare_sixel_ids);
+
+ for (ii = 0U; ii < MAX_SIXEL_GRAPHICS; ii++) {
+ graphic = ordered_graphics[ii];
+ if (!graphic->valid || graphic->bufferid != screen->whichBuf)
+ continue;
+
+ x = (leftcol - graphic->charcol) * FontWidth(screen);
+ y = (toprow - graphic->charrow) * FontHeight(screen);
+ w = ncols * FontWidth(screen);
+ h = nrows * FontHeight(screen);
+
+ xbase = (screen->border
+ + graphic->charcol * FontWidth(screen));
+ ybase = (screen->border
+ + (graphic->charrow - screen->topline) * FontHeight(screen));
+
+ if (xbase + x + w + screen->border > FullWidth(screen))
+ w = FullWidth(screen) - (xbase + x + screen->border);
+ if (ybase + y + h + screen->border > FullHeight(screen))
+ h = FullHeight(screen) - (ybase + y + screen->border);
+ else if (ybase + y < screen->border) {
+ int diff = screen->border - (ybase + y);
+ y += diff;
+ h -= diff;
+ }
+
+ TRACE(("graphics refresh: screen->topline=%d leftcol=%d toprow=%d nrows=%d ncols=%d x=%d y=%d w=%d h=%d xbase=%d ybase=%d\n",
+ screen->topline,
+ leftcol, toprow,
+ nrows, ncols,
+ x, y, w, h,
+ xbase, ybase));
+ refresh_sixel_graphic(screen, graphic, xbase, ybase, x, y, w, h);
+ }
+}
+
+extern void
+refresh_modified_displayed_graphics(TScreen const *screen)
+{
+ SixelGraphic *graphic;
+ unsigned int ii;
+ int leftcol, toprow;
+ int nrows, ncols;
+ int x, y, w, h;
+ int xbase, ybase;
+
+ for (ii = 0U; ii < MAX_SIXEL_GRAPHICS; ii++) {
+ graphic = &sixel_graphics[ii];
+ if (!graphic->valid || graphic->bufferid != screen->whichBuf || !graphic->dirty)
+ continue;
+
+ leftcol = graphic->charcol;
+ toprow = graphic->charrow;
+ nrows = (((graphic->actual_height * graphic->pixh)
+ + FontHeight(screen) - 1)
+ / FontHeight(screen));
+ ncols = (((graphic->actual_width * graphic->pixw)
+ + FontWidth(screen) - 1)
+ / FontWidth(screen));
+
+ x = (leftcol - graphic->charcol) * FontWidth(screen);
+ y = (toprow - graphic->charrow) * FontHeight(screen);
+ w = ncols * FontWidth(screen);
+ h = nrows * FontHeight(screen);
+
+ xbase = (screen->border
+ + graphic->charcol * FontWidth(screen));
+ ybase = (screen->border
+ + (graphic->charrow - screen->topline) * FontHeight(screen));
+
+ if (xbase + x + w + screen->border > FullWidth(screen))
+ w = FullWidth(screen) - (xbase + x + screen->border);
+ if (ybase + y + h + screen->border > FullHeight(screen))
+ h = FullHeight(screen) - (ybase + y + screen->border);
+ else if (ybase + y < screen->border) {
+ int diff = screen->border - (ybase + y);
+ y += diff;
+ h -= diff;
+ }
+
+ TRACE(("full graphics refresh: screen->topline=%d leftcol=%d toprow=%d nrows=%d ncols=%d x=%d y=%d w=%d h=%d xbase=%d ybase=%d\n",
+ screen->topline,
+ leftcol, toprow,
+ nrows, ncols,
+ x, y, w, h,
+ xbase, ybase));
+ refresh_sixel_graphic(screen, graphic, xbase, ybase, x, y, w, h);
+ graphic->dirty = 0;
+ }
+}
+
+extern void
+scroll_displayed_graphics(int rows)
+{
+ SixelGraphic *graphic;
+ unsigned int ii;
+
+ TRACE(("graphics scroll: moving all up %d rows\n", rows));
+ for (ii = 0U; ii < MAX_SIXEL_GRAPHICS; ii++) {
+ graphic = &sixel_graphics[ii];
+ if (!graphic->valid)
+ continue;
+
+ graphic->charrow -= rows;
+ }
+}
+
+extern void
+pixelarea_clear_displayed_graphics(TScreen const *screen,
+ int winx,
+ int winy,
+ int w,
+ int h)
+{
+ SixelGraphic *graphic;
+ unsigned int ii;
+ int x, y;
+
+ for (ii = 0U; ii < MAX_SIXEL_GRAPHICS; ii++) {
+ graphic = &sixel_graphics[ii];
+ if (!graphic->valid)
+ continue;
+
+ x = winx - graphic->charcol * FontWidth(screen);
+ y = winy - graphic->charrow * FontHeight(screen);
+
+ TRACE(("pixelarea graphics erase: screen->topline=%d winx=%d winy=%d w=%d h=%d x=%d y=%d\n",
+ screen->topline,
+ winx, winy,
+ w, h,
+ x, y));
+ erase_sixel_graphic(graphic, x, y, w, h);
+ }
+}
+
+extern void
+chararea_clear_displayed_graphics(TScreen const *screen,
+ int leftcol,
+ int toprow,
+ int ncols,
+ int nrows)
+{
+ int x, y, w, h;
+
+ x = leftcol * FontWidth(screen);
+ y = toprow * FontHeight(screen);
+ w = ncols * FontWidth(screen);
+ h = nrows * FontHeight(screen);
+
+ TRACE(("chararea clear graphics: screen->topline=%d leftcol=%d toprow=%d nrows=%d ncols=%d x=%d y=%d w=%d h=%d\n",
+ screen->topline,
+ leftcol, toprow,
+ nrows, ncols,
+ x, y, w, h));
+ pixelarea_clear_displayed_graphics(screen, x, y, w, h);
+}
+
+extern void
+reset_displayed_graphics(TScreen const *screen)
+{
+ SixelGraphic *graphic;
+ unsigned int ii;
+
+ init_color_registers(shared_color_registers, screen->terminal_id);
+ TRACE(("resetting all sixel graphics\n"));
+ for (ii = 0U; ii < MAX_SIXEL_GRAPHICS; ii++) {
+ graphic = &sixel_graphics[ii];
+ graphic->valid = 0;
+ }
+}