15104ee6eSmrg/* $XTermId: graphics.c,v 1.137 2024/12/01 20:21:19 tom Exp $ */ 2e0a2b6dfSmrg 3e0a2b6dfSmrg/* 404b94745Smrg * Copyright 2013-2023,2024 by Thomas E. Dickey 504b94745Smrg * Copyright 2013-2022,2023 by Ross Combs 6e0a2b6dfSmrg * 7e0a2b6dfSmrg * All Rights Reserved 8e0a2b6dfSmrg * 9e0a2b6dfSmrg * Permission is hereby granted, free of charge, to any person obtaining a 10e0a2b6dfSmrg * copy of this software and associated documentation files (the 11e0a2b6dfSmrg * "Software"), to deal in the Software without restriction, including 12e0a2b6dfSmrg * without limitation the rights to use, copy, modify, merge, publish, 13e0a2b6dfSmrg * distribute, sublicense, and/or sell copies of the Software, and to 14e0a2b6dfSmrg * permit persons to whom the Software is furnished to do so, subject to 15e0a2b6dfSmrg * the following conditions: 16e0a2b6dfSmrg * 17e0a2b6dfSmrg * The above copyright notice and this permission notice shall be included 18e0a2b6dfSmrg * in all copies or substantial portions of the Software. 19e0a2b6dfSmrg * 20e0a2b6dfSmrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 21e0a2b6dfSmrg * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 22e0a2b6dfSmrg * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 23e0a2b6dfSmrg * IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY 24e0a2b6dfSmrg * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 25e0a2b6dfSmrg * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 26e0a2b6dfSmrg * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 27e0a2b6dfSmrg * 28e0a2b6dfSmrg * Except as contained in this notice, the name(s) of the above copyright 29e0a2b6dfSmrg * holders shall not be used in advertising or otherwise to promote the 30e0a2b6dfSmrg * sale, use or other dealings in this Software without prior written 31e0a2b6dfSmrg * authorization. 32e0a2b6dfSmrg */ 33e0a2b6dfSmrg 34e0a2b6dfSmrg#include <xterm.h> 35e0a2b6dfSmrg 36e0a2b6dfSmrg#include <stdio.h> 37e0a2b6dfSmrg#include <ctype.h> 38e0a2b6dfSmrg#include <stdlib.h> 39e0a2b6dfSmrg 40e0a2b6dfSmrg#include <data.h> 41e0a2b6dfSmrg#include <ptyx.h> 42e0a2b6dfSmrg 43e0a2b6dfSmrg#include <assert.h> 44e0a2b6dfSmrg#include <graphics.h> 45e0a2b6dfSmrg 4604b94745Smrg#define OPT_INHERIT_COLORS 0 4704b94745Smrg 4804b94745Smrg#if OPT_REGIS_GRAPHICS 4904b94745Smrg#include <graphics_regis.h> 5004b94745Smrg#endif 5104b94745Smrg 52894e0ac8Smrg#undef DUMP_BITMAP 53894e0ac8Smrg#undef DUMP_COLORS 54894e0ac8Smrg#undef DEBUG_PALETTE 55894e0ac8Smrg#undef DEBUG_PIXEL 56e0a2b6dfSmrg#undef DEBUG_REFRESH 57e0a2b6dfSmrg 5801037d57Smrg/* 5901037d57Smrg * graphics TODO list 6001037d57Smrg * 61e0a2b6dfSmrg * ReGIS: 622e4f8982Smrg * - ship a default alphabet zero font instead of scaling Xft fonts 63913cc679Smrg * - input cursors 64913cc679Smrg * - output cursors 65913cc679Smrg * - mouse/tablet/arrow-key input 662e4f8982Smrg * - fix graphic pages for ReGIS -- they should also apply to text and sixel graphics 6701037d57Smrg * - fix interpolated curves to more closely match implementation (identical despite direction and starting point) 6801037d57Smrg * - non-ASCII alphabets 692e4f8982Smrg * - enter/leave anywhere in a command 702e4f8982Smrg * - locator key definitions (DECLKD) 71894e0ac8Smrg * - command display mode 72913cc679Smrg * - re-rasterization on window resize 73894e0ac8Smrg * - macros 7401037d57Smrg * - improved fills for narrow angles (track actual lines not just pixels) 75913cc679Smrg * - hardcopy/screen-capture support (need dialog of some sort for safety) 76913cc679Smrg * - error reporting 7701037d57Smrg * 78e0a2b6dfSmrg * sixel: 79894e0ac8Smrg * - fix problem where new_row < 0 during sixel parsing (see FIXME) 8001037d57Smrg * - screen-capture support (need dialog of some sort for safety) 8101037d57Smrg * 82894e0ac8Smrg * VT55/VT105 waveform graphics 83894e0ac8Smrg * - everything 8401037d57Smrg * 8501037d57Smrg * Tektronix: 8601037d57Smrg * - color (VT340 4014 emulation, 41xx, IRAF GTERM, and also MS-DOS Kermit color support) 8701037d57Smrg * - polygon fill (41xx) 8801037d57Smrg * - clear area extension 8901037d57Smrg * - area fill extension 9001037d57Smrg * - pixel operations (RU/RS/RP) 9101037d57Smrg * - research other 41xx and 42xx extensions 9201037d57Smrg * 9301037d57Smrg * common graphics features: 94894e0ac8Smrg * - handle light/dark screen modes (CSI?5[hl]) 95894e0ac8Smrg * - update text fg/bg color which overlaps images 9601037d57Smrg * - handle graphic updates in scroll regions (verify effect on graphics) 97894e0ac8Smrg * - handle rectangular area copies (verify they work with graphics) 9801037d57Smrg * - invalidate graphics under graphic if same origin, at least as big, and bg not transparent 9901037d57Smrg * - invalidate graphic if completely scrolled past end of scrollback 10001037d57Smrg * - invalidate graphic if all pixels are transparent/erased 10101037d57Smrg * - invalidate graphic if completely scrolled out of alt buffer 10201037d57Smrg * - posturize requested colors to match hardware palettes (e.g. only four possible shades on VT240) 103894e0ac8Smrg * - color register report/restore 10401037d57Smrg * - ability to select/copy graphics for pasting in other programs 10501037d57Smrg * - ability to show non-scroll-mode sixel graphics in a separate window 10601037d57Smrg * - ability to show ReGIS graphics in a separate window 10701037d57Smrg * - ability to show Tektronix graphics in VT100 window 1082e4f8982Smrg * - truncate graphics at bottom edge of terminal? 1092e4f8982Smrg * - locator events (DECEFR DECSLE DECELR DECLRP) 1102e4f8982Smrg * - locator controller mode (CSI6i / CSI7i) 11101037d57Smrg * 11201037d57Smrg * new escape sequences: 11301037d57Smrg * - way to query text font size without "window ops" (or make "window ops" permissions more fine grained) 114894e0ac8Smrg * - way to query and set the number of graphics pages 11501037d57Smrg * 116894e0ac8Smrg * ReGIS extensions: 11701037d57Smrg * - non-integer text scaling 118913cc679Smrg * - free distortionless text rotation (vs. simulating the distortion and aligning to 45deg increments) 11901037d57Smrg * - font characteristics: bold/underline/italic 12001037d57Smrg * - remove/increase arbitrary limits (pattern size, pages, alphabets, stack size, font names, etc.) 12101037d57Smrg * - shade/fill with borders 12201037d57Smrg * - sprites (copy portion of page into/out of buffer with scaling and rotation) 12301037d57Smrg * - ellipses 12401037d57Smrg * - 2D patterns 12501037d57Smrg * - option to set actual graphic size (not just coordinate range) 12601037d57Smrg * - gradients (for lines and fills) 127894e0ac8Smrg * - line width (RLogin has this and it is mentioned in docs for the DEC ReGIS to Postscript converter) 128894e0ac8Smrg * - transparency 129894e0ac8Smrg * - background color as stackable write control 130894e0ac8Smrg * - true color (virtual color registers created upon lookup) 131894e0ac8Smrg * - anti-aliasing 132913cc679Smrg * - variable-width (proportional) text 133e0a2b6dfSmrg */ 134e0a2b6dfSmrg 135e0a2b6dfSmrg/* font sizes: 136e0a2b6dfSmrg * VT510: 137e0a2b6dfSmrg * 80 Columns 132 Columns Maximum Number of Lines 138e0a2b6dfSmrg * 10 x 16 6 x 16 26 lines + keyboard indicator line 139894e0ac8Smrg * 10 x 13 6 x 13 26 lines + keyboard indicator line 140e0a2b6dfSmrg * 10 x 10 6 x 10 42 lines + keyboard indicator line 141e0a2b6dfSmrg * 10 x 8 6 x 8 53 lines + keyboard indicator line 14201037d57Smrg */ 14301037d57Smrg 14401037d57Smrgtypedef struct allocated_color_register { 14501037d57Smrg struct allocated_color_register *next; 14601037d57Smrg Pixel pix; 14701037d57Smrg short r, g, b; 14801037d57Smrg} AllocatedColorRegister; 14901037d57Smrg 15001037d57Smrg#define LOOKUP_WIDTH 16 15101037d57Smrgstatic AllocatedColorRegister *allocated_colors[LOOKUP_WIDTH][LOOKUP_WIDTH][LOOKUP_WIDTH]; 152e0a2b6dfSmrg 153894e0ac8Smrg#define FOR_EACH_SLOT(ii) for (ii = 0U; ii < MAX_GRAPHICS; ii++) 154894e0ac8Smrg 155894e0ac8Smrgstatic ColorRegister *shared_color_registers; 156894e0ac8Smrgstatic Graphic *displayed_graphics[MAX_GRAPHICS]; 157894e0ac8Smrgstatic unsigned next_graphic_id = 0U; 158f2e35a3aSmrgstatic unsigned used_graphics; /* 0 to MAX_GRAPHICS */ 159f2e35a3aSmrg 1604419d26bSmrgstatic int valid_graphics; 1614419d26bSmrgstatic GC graphics_gc; 1624419d26bSmrgstatic XGCValues xgcv; 1634419d26bSmrgstatic ColorRegister last_color; 1644419d26bSmrgstatic ColorRegister gc_color; 1654419d26bSmrg 166f2e35a3aSmrg#define DiffColor(this,that) \ 167f2e35a3aSmrg (this.r != that.r || \ 168f2e35a3aSmrg this.g != that.g || \ 169f2e35a3aSmrg this.b != that.b) 170f2e35a3aSmrg 171f2e35a3aSmrgstatic ColorRegister null_color = 172f2e35a3aSmrg{-1, -1, -1}; 173894e0ac8Smrg 174894e0ac8Smrgstatic ColorRegister * 175894e0ac8SmrgallocRegisters(void) 176e0a2b6dfSmrg{ 177894e0ac8Smrg return TypeCallocN(ColorRegister, MAX_COLOR_REGISTERS); 178894e0ac8Smrg} 179894e0ac8Smrg 180894e0ac8Smrgstatic Graphic * 181894e0ac8SmrgfreeGraphic(Graphic *obj) 182894e0ac8Smrg{ 183894e0ac8Smrg if (obj) { 184f2e35a3aSmrg free(obj->pixels); 185f2e35a3aSmrg free(obj->private_color_registers); 186894e0ac8Smrg free(obj); 187894e0ac8Smrg } 188894e0ac8Smrg return NULL; 189894e0ac8Smrg} 190894e0ac8Smrg 191894e0ac8Smrgstatic Graphic * 19201037d57SmrgallocGraphic(int max_w, int max_h) 193894e0ac8Smrg{ 194894e0ac8Smrg Graphic *result = TypeCalloc(Graphic); 195894e0ac8Smrg if (result) { 19601037d57Smrg result->max_width = max_w; 19701037d57Smrg result->max_height = max_h; 19801037d57Smrg if (!(result->pixels = TypeCallocN(RegisterNum, 19901037d57Smrg (size_t) max_w * (size_t) max_h))) { 200894e0ac8Smrg result = freeGraphic(result); 201894e0ac8Smrg } else if (!(result->private_color_registers = allocRegisters())) { 202894e0ac8Smrg result = freeGraphic(result); 203e0a2b6dfSmrg } 204e0a2b6dfSmrg } 205894e0ac8Smrg return result; 206894e0ac8Smrg} 207e0a2b6dfSmrg 208f2e35a3aSmrg#define getActiveSlot(n) \ 209f2e35a3aSmrg (((n) < MAX_GRAPHICS && \ 210f2e35a3aSmrg displayed_graphics[n] && \ 211f2e35a3aSmrg displayed_graphics[n]->valid) \ 212f2e35a3aSmrg ? displayed_graphics[n] \ 213f2e35a3aSmrg : NULL) 214e0a2b6dfSmrg 215894e0ac8Smrgstatic Graphic * 21601037d57SmrggetInactiveSlot(const TScreen *screen, unsigned n) 217894e0ac8Smrg{ 218894e0ac8Smrg if (n < MAX_GRAPHICS && 219894e0ac8Smrg (!displayed_graphics[n] || 220894e0ac8Smrg !displayed_graphics[n]->valid)) { 221894e0ac8Smrg if (!displayed_graphics[n]) { 22201037d57Smrg displayed_graphics[n] = allocGraphic(screen->graphics_max_wide, 22301037d57Smrg screen->graphics_max_high); 224f2e35a3aSmrg used_graphics += (displayed_graphics[n] != NULL); 225894e0ac8Smrg } 226894e0ac8Smrg return displayed_graphics[n]; 227894e0ac8Smrg } 228894e0ac8Smrg return NULL; 229894e0ac8Smrg} 230894e0ac8Smrg 231894e0ac8Smrgstatic ColorRegister * 232894e0ac8SmrggetSharedRegisters(void) 233894e0ac8Smrg{ 234894e0ac8Smrg if (!shared_color_registers) 235894e0ac8Smrg shared_color_registers = allocRegisters(); 236894e0ac8Smrg return shared_color_registers; 237894e0ac8Smrg} 238e0a2b6dfSmrg 239e0a2b6dfSmrgstatic void 240894e0ac8SmrgdeactivateSlot(unsigned n) 241e0a2b6dfSmrg{ 242f2e35a3aSmrg if ((n < MAX_GRAPHICS) && displayed_graphics[n]) { 243894e0ac8Smrg displayed_graphics[n] = freeGraphic(displayed_graphics[n]); 244f2e35a3aSmrg used_graphics--; 245894e0ac8Smrg } 246894e0ac8Smrg} 247e0a2b6dfSmrg 248894e0ac8Smrgextern RegisterNum 249894e0ac8Smrgread_pixel(Graphic *graphic, int x, int y) 250894e0ac8Smrg{ 2514419d26bSmrg return (((x) >= 0 && 2524419d26bSmrg (x) < (graphic)->actual_width && 2534419d26bSmrg (y) >= 0 && 2544419d26bSmrg (y) < (graphic)->actual_height) 2554419d26bSmrg ? (graphic)->pixels[(y) * (graphic)->max_width + (x)] 2564419d26bSmrg : (RegisterNum) COLOR_HOLE); 257e0a2b6dfSmrg} 258e0a2b6dfSmrg 25901037d57Smrg#define _draw_pixel(G, X, Y, C) \ 26001037d57Smrg do { \ 2614419d26bSmrg unsigned _cell = (unsigned)((Y) * (G)->max_width + (X)); \ 2624419d26bSmrg SetSpixel(G, _cell, (RegisterNum) (C)); \ 26301037d57Smrg } while (0) 26401037d57Smrg 265894e0ac8Smrgvoid 266894e0ac8Smrgdraw_solid_pixel(Graphic *graphic, int x, int y, unsigned color) 267e0a2b6dfSmrg{ 268894e0ac8Smrg assert(color <= MAX_COLOR_REGISTERS); 269e0a2b6dfSmrg 270894e0ac8Smrg#ifdef DEBUG_PIXEL 271894e0ac8Smrg TRACE(("drawing pixel at %d,%d color=%hu (hole=%hu, [%d,%d,%d])\n", 272894e0ac8Smrg x, 273894e0ac8Smrg y, 274e0a2b6dfSmrg color, 275894e0ac8Smrg COLOR_HOLE, 276e0a2b6dfSmrg ((color != COLOR_HOLE) 277894e0ac8Smrg ? (unsigned) graphic->color_registers[color].r : 0U), 278e0a2b6dfSmrg ((color != COLOR_HOLE) 279894e0ac8Smrg ? (unsigned) graphic->color_registers[color].g : 0U), 280e0a2b6dfSmrg ((color != COLOR_HOLE) 281894e0ac8Smrg ? (unsigned) graphic->color_registers[color].b : 0U))); 282894e0ac8Smrg#endif 283894e0ac8Smrg if (x >= 0 && x < graphic->actual_width && 284894e0ac8Smrg y >= 0 && y < graphic->actual_height) { 28501037d57Smrg _draw_pixel(graphic, x, y, color); 286894e0ac8Smrg if (color < MAX_COLOR_REGISTERS) 28704b94745Smrg graphic->color_registers_used[color] = True; 288894e0ac8Smrg } 289894e0ac8Smrg} 290894e0ac8Smrg 291894e0ac8Smrgvoid 292894e0ac8Smrgdraw_solid_rectangle(Graphic *graphic, int x1, int y1, int x2, int y2, unsigned color) 293894e0ac8Smrg{ 294894e0ac8Smrg int x, y; 295894e0ac8Smrg int tmp; 296894e0ac8Smrg 297894e0ac8Smrg assert(color <= MAX_COLOR_REGISTERS); 298894e0ac8Smrg 299894e0ac8Smrg if (x1 > x2) { 300894e0ac8Smrg EXCHANGE(x1, x2, tmp); 301894e0ac8Smrg } 302894e0ac8Smrg if (y1 > y2) { 303894e0ac8Smrg EXCHANGE(y1, y2, tmp); 304894e0ac8Smrg } 305894e0ac8Smrg 30601037d57Smrg if (x2 < 0 || x1 >= graphic->actual_width || 30701037d57Smrg y2 < 0 || y1 >= graphic->actual_height) 30801037d57Smrg return; 30901037d57Smrg 31001037d57Smrg if (x1 < 0) 31101037d57Smrg x1 = 0; 31201037d57Smrg if (x2 >= graphic->actual_width) 31301037d57Smrg x2 = graphic->actual_width - 1; 31401037d57Smrg if (y1 < 0) 31501037d57Smrg y1 = 0; 31601037d57Smrg if (y2 >= graphic->actual_height) 31701037d57Smrg y2 = graphic->actual_height - 1; 31801037d57Smrg 31901037d57Smrg if (color < MAX_COLOR_REGISTERS) 32004b94745Smrg graphic->color_registers_used[color] = True; 321894e0ac8Smrg for (y = y1; y <= y2; y++) 32201037d57Smrg for (x = x1; x <= x2; x++) 32301037d57Smrg _draw_pixel(graphic, x, y, color); 324894e0ac8Smrg} 325894e0ac8Smrg 32601037d57Smrgvoid 32701037d57Smrgcopy_overlapping_area(Graphic *graphic, int src_ul_x, int src_ul_y, 32801037d57Smrg int dst_ul_x, int dst_ul_y, unsigned w, unsigned h, 32901037d57Smrg unsigned default_color) 33001037d57Smrg{ 33101037d57Smrg int sx, ex, dx; 33201037d57Smrg int sy, ey, dy; 33301037d57Smrg int xx, yy; 33401037d57Smrg RegisterNum color; 33501037d57Smrg 33601037d57Smrg if (dst_ul_x <= src_ul_x) { 33701037d57Smrg sx = 0; 33801037d57Smrg ex = (int) w - 1; 33901037d57Smrg dx = +1; 34001037d57Smrg } else { 34101037d57Smrg sx = (int) w - 1; 34201037d57Smrg ex = 0; 34301037d57Smrg dx = -1; 34401037d57Smrg } 34501037d57Smrg 34601037d57Smrg if (dst_ul_y <= src_ul_y) { 34701037d57Smrg sy = 0; 34801037d57Smrg ey = (int) h - 1; 34901037d57Smrg dy = +1; 35001037d57Smrg } else { 35101037d57Smrg sy = (int) h - 1; 35201037d57Smrg ey = 0; 35301037d57Smrg dy = -1; 35401037d57Smrg } 35501037d57Smrg 35601037d57Smrg for (yy = sy; yy != ey + dy; yy += dy) { 3572e4f8982Smrg int dst_y = dst_ul_y + yy; 3582e4f8982Smrg int src_y = src_ul_y + yy; 35901037d57Smrg if (dst_y < 0 || dst_y >= (int) graphic->actual_height) 36001037d57Smrg continue; 36101037d57Smrg 36201037d57Smrg for (xx = sx; xx != ex + dx; xx += dx) { 3632e4f8982Smrg int dst_x = dst_ul_x + xx; 3642e4f8982Smrg int src_x = src_ul_x + xx; 3654419d26bSmrg int cell; 36601037d57Smrg if (dst_x < 0 || dst_x >= (int) graphic->actual_width) 36701037d57Smrg continue; 36801037d57Smrg 36901037d57Smrg if (src_x < 0 || src_x >= (int) graphic->actual_width || 37001037d57Smrg src_y < 0 || src_y >= (int) graphic->actual_height) 37101037d57Smrg color = (RegisterNum) default_color; 37201037d57Smrg else 37301037d57Smrg color = graphic->pixels[(unsigned) (src_y * 37401037d57Smrg graphic->max_width) + 37501037d57Smrg (unsigned) src_x]; 37601037d57Smrg 3774419d26bSmrg cell = (int) ((unsigned) (dst_y * graphic->max_width) + 3784419d26bSmrg (unsigned) dst_x); 3794419d26bSmrg SetSpixel(graphic, cell, color); 38001037d57Smrg } 38101037d57Smrg } 38201037d57Smrg} 38301037d57Smrg 3844419d26bSmrg#define set_color_register(color_registers, color, pr, pg, pb) \ 3854419d26bSmrgdo { \ 38604b94745Smrg assert(color <= MAX_COLOR_REGISTERS); \ 38704b94745Smrg { \ 3884419d26bSmrg ColorRegister *reg = &color_registers[color]; \ 3894419d26bSmrg reg->r = (short) pr; \ 3904419d26bSmrg reg->g = (short) pg; \ 3914419d26bSmrg reg->b = (short) pb; \ 39204b94745Smrg } \ 3934419d26bSmrg} while (0) 394e0a2b6dfSmrg 395894e0ac8Smrg/* Graphics which don't use private colors will act as if they are using a 396894e0ac8Smrg * device-wide color palette. 397894e0ac8Smrg */ 398894e0ac8Smrgstatic void 399894e0ac8Smrgset_shared_color_register(unsigned color, int r, int g, int b) 400894e0ac8Smrg{ 401894e0ac8Smrg unsigned ii; 402894e0ac8Smrg 403894e0ac8Smrg assert(color < MAX_COLOR_REGISTERS); 404894e0ac8Smrg 405894e0ac8Smrg set_color_register(getSharedRegisters(), color, r, g, b); 406894e0ac8Smrg 407f2e35a3aSmrg if (!used_graphics) 408f2e35a3aSmrg return; 409f2e35a3aSmrg 410894e0ac8Smrg FOR_EACH_SLOT(ii) { 4112e4f8982Smrg Graphic *graphic; 4122e4f8982Smrg 413894e0ac8Smrg if (!(graphic = getActiveSlot(ii))) 414894e0ac8Smrg continue; 415894e0ac8Smrg if (graphic->private_colors) 416894e0ac8Smrg continue; 417894e0ac8Smrg 418894e0ac8Smrg if (graphic->color_registers_used[ii]) { 4194419d26bSmrg graphic->dirty = True; 420894e0ac8Smrg } 421894e0ac8Smrg } 422894e0ac8Smrg} 423894e0ac8Smrg 42404b94745Smrgvoid 42504b94745Smrgfetch_color_register(Graphic *graphic, 42604b94745Smrg unsigned color, 42704b94745Smrg ColorRegister *reg) 42804b94745Smrg{ 42904b94745Smrg assert(color < MAX_COLOR_REGISTERS); 43004b94745Smrg 43104b94745Smrg reg->r = -1; 43204b94745Smrg reg->g = -1; 43304b94745Smrg reg->b = -1; 43404b94745Smrg 43504b94745Smrg if (graphic->color_registers_used[color]) { 43604b94745Smrg if (graphic->private_colors) { 43704b94745Smrg *reg = graphic->private_color_registers[color]; 43804b94745Smrg } else { 43904b94745Smrg *reg = getSharedRegisters()[color]; 44004b94745Smrg } 44104b94745Smrg } 44204b94745Smrg} 44304b94745Smrg 444894e0ac8Smrgvoid 445894e0ac8Smrgupdate_color_register(Graphic *graphic, 446894e0ac8Smrg unsigned color, 447894e0ac8Smrg int r, 448894e0ac8Smrg int g, 449894e0ac8Smrg int b) 450894e0ac8Smrg{ 451894e0ac8Smrg assert(color < MAX_COLOR_REGISTERS); 452894e0ac8Smrg 453894e0ac8Smrg if (graphic->private_colors) { 454894e0ac8Smrg set_color_register(graphic->private_color_registers, 455894e0ac8Smrg color, r, g, b); 456894e0ac8Smrg if (graphic->color_registers_used[color]) { 4574419d26bSmrg graphic->dirty = True; 458894e0ac8Smrg } 45904b94745Smrg graphic->color_registers_used[color] = True; 460894e0ac8Smrg } else { 461894e0ac8Smrg set_shared_color_register(color, r, g, b); 462894e0ac8Smrg } 463894e0ac8Smrg} 464894e0ac8Smrg 465894e0ac8Smrg#define SQUARE(X) ( (X) * (X) ) 466894e0ac8Smrg 467894e0ac8SmrgRegisterNum 468894e0ac8Smrgfind_color_register(ColorRegister const *color_registers, int r, int g, int b) 469894e0ac8Smrg{ 470894e0ac8Smrg unsigned i; 471894e0ac8Smrg unsigned closest_index; 472894e0ac8Smrg unsigned closest_distance; 473894e0ac8Smrg 474894e0ac8Smrg /* I have no idea what algorithm DEC used for this. 475894e0ac8Smrg * The documentation warns that it is unpredictable, especially with values 476894e0ac8Smrg * far away from any allocated color so it is probably a very simple 47701037d57Smrg * heuristic rather than something fancy like finding the minimum distance 478894e0ac8Smrg * in a linear perceptive color space. 479894e0ac8Smrg */ 480894e0ac8Smrg closest_index = MAX_COLOR_REGISTERS; 481894e0ac8Smrg closest_distance = 0U; 482894e0ac8Smrg for (i = 0U; i < MAX_COLOR_REGISTERS; i++) { 4832e4f8982Smrg unsigned d = (unsigned) (SQUARE(2 * (color_registers[i].r - r)) + 4842e4f8982Smrg SQUARE(3 * (color_registers[i].g - g)) + 4852e4f8982Smrg SQUARE(1 * (color_registers[i].b - b))); 486894e0ac8Smrg if (closest_index == MAX_COLOR_REGISTERS || d < closest_distance) { 487894e0ac8Smrg closest_index = i; 488894e0ac8Smrg closest_distance = d; 489894e0ac8Smrg } 490894e0ac8Smrg } 491894e0ac8Smrg 492894e0ac8Smrg TRACE(("found closest color register to %d,%d,%d: %u (distance %u value %d,%d,%d)\n", 493894e0ac8Smrg r, g, b, 494894e0ac8Smrg closest_index, 495894e0ac8Smrg closest_distance, 496894e0ac8Smrg color_registers[closest_index].r, 497894e0ac8Smrg color_registers[closest_index].g, 498894e0ac8Smrg color_registers[closest_index].b)); 499894e0ac8Smrg return (RegisterNum) closest_index; 500894e0ac8Smrg} 501894e0ac8Smrg 50204b94745Smrg#if OPT_INHERIT_COLORS 503e0a2b6dfSmrgstatic void 50404b94745Smrgcopy_color_registers(Graphic *target, Graphic *source) 505e0a2b6dfSmrg{ 50604b94745Smrg memcpy(target->color_registers_used, 50704b94745Smrg source->color_registers_used, 50804b94745Smrg sizeof(Boolean) * MAX_COLOR_REGISTERS); 509e0a2b6dfSmrg 51004b94745Smrg memcpy(target->private_color_registers, 51104b94745Smrg source->color_registers, 51204b94745Smrg sizeof(ColorRegister) * MAX_COLOR_REGISTERS); 51304b94745Smrg} 51404b94745Smrg#endif 51504b94745Smrg 51604b94745Smrgstatic void 51704b94745Smrginit_color_registers(TScreen const *screen, ColorRegister *color_registers) 51804b94745Smrg{ 51904b94745Smrg const int graphics_termid = GraphicsTermId(screen); 52004b94745Smrg 52104b94745Smrg TRACE(("setting initial colors for terminal %d\n", graphics_termid)); 52204b94745Smrg memset(color_registers, 52304b94745Smrg 0, 52404b94745Smrg sizeof(ColorRegister) * MAX_COLOR_REGISTERS); 525e0a2b6dfSmrg 526e0a2b6dfSmrg /* 527e0a2b6dfSmrg * default color registers: 528e0a2b6dfSmrg * (mono) (color) 529e0a2b6dfSmrg * VK100/GIGI (fixed) 530e0a2b6dfSmrg * VT125: 531e0a2b6dfSmrg * 0: 0% 0% 532e0a2b6dfSmrg * 1: 33% blue 533e0a2b6dfSmrg * 2: 66% red 534e0a2b6dfSmrg * 3: 100% green 535e0a2b6dfSmrg * VT240: 536e0a2b6dfSmrg * 0: 0% 0% 537e0a2b6dfSmrg * 1: 33% blue 538e0a2b6dfSmrg * 2: 66% red 539e0a2b6dfSmrg * 3: 100% green 540e0a2b6dfSmrg * VT241: 541e0a2b6dfSmrg * 0: 0% 0% 542e0a2b6dfSmrg * 1: 33% blue 543e0a2b6dfSmrg * 2: 66% red 544e0a2b6dfSmrg * 3: 100% green 545e0a2b6dfSmrg * VT330: 546e0a2b6dfSmrg * 0: 0% 0% (bg for light on dark mode) 547e0a2b6dfSmrg * 1: 33% blue (red?) 548e0a2b6dfSmrg * 2: 66% red (green?) 549e0a2b6dfSmrg * 3: 100% green (yellow?) (fg for light on dark mode) 550e0a2b6dfSmrg * VT340: 551e0a2b6dfSmrg * 0: 0% 0% (bg for light on dark mode) 552e0a2b6dfSmrg * 1: 14% blue 553e0a2b6dfSmrg * 2: 29% red 554e0a2b6dfSmrg * 3: 43% green 555e0a2b6dfSmrg * 4: 57% magenta 556e0a2b6dfSmrg * 5: 71% cyan 557e0a2b6dfSmrg * 6: 86% yellow 558e0a2b6dfSmrg * 7: 100% 50% (fg for light on dark mode) 559e0a2b6dfSmrg * 8: 0% 25% 560e0a2b6dfSmrg * 9: 14% gray-blue 561e0a2b6dfSmrg * 10: 29% gray-red 562e0a2b6dfSmrg * 11: 43% gray-green 563e0a2b6dfSmrg * 12: 57% gray-magenta 564e0a2b6dfSmrg * 13: 71% gray-cyan 565e0a2b6dfSmrg * 14: 86% gray-yellow 566894e0ac8Smrg * 15: 100% 75% ("white") 567894e0ac8Smrg * VT382: 568894e0ac8Smrg * ? (FIXME: B&W only?) 569e0a2b6dfSmrg * dxterm: 570e0a2b6dfSmrg * ? 571e0a2b6dfSmrg */ 572f2e35a3aSmrg switch (graphics_termid) { 573e0a2b6dfSmrg case 125: 574e0a2b6dfSmrg case 241: 575894e0ac8Smrg set_color_register(color_registers, 0, 0, 0, 0); 576894e0ac8Smrg set_color_register(color_registers, 1, 0, 0, 100); 577894e0ac8Smrg set_color_register(color_registers, 2, 0, 100, 0); 578894e0ac8Smrg set_color_register(color_registers, 3, 100, 0, 0); 579e0a2b6dfSmrg break; 580e0a2b6dfSmrg case 240: 581e0a2b6dfSmrg case 330: 582894e0ac8Smrg set_color_register(color_registers, 0, 0, 0, 0); 583894e0ac8Smrg set_color_register(color_registers, 1, 33, 33, 33); 584894e0ac8Smrg set_color_register(color_registers, 2, 66, 66, 66); 585894e0ac8Smrg set_color_register(color_registers, 3, 100, 100, 100); 586e0a2b6dfSmrg break; 587e0a2b6dfSmrg case 340: 588e0a2b6dfSmrg default: 589894e0ac8Smrg set_color_register(color_registers, 0, 0, 0, 0); 590894e0ac8Smrg set_color_register(color_registers, 1, 20, 20, 80); 591894e0ac8Smrg set_color_register(color_registers, 2, 80, 13, 13); 592894e0ac8Smrg set_color_register(color_registers, 3, 20, 80, 20); 593894e0ac8Smrg set_color_register(color_registers, 4, 80, 20, 80); 594894e0ac8Smrg set_color_register(color_registers, 5, 20, 80, 80); 595894e0ac8Smrg set_color_register(color_registers, 6, 80, 80, 20); 596894e0ac8Smrg set_color_register(color_registers, 7, 53, 53, 53); 597894e0ac8Smrg set_color_register(color_registers, 8, 26, 26, 26); 598894e0ac8Smrg set_color_register(color_registers, 9, 33, 33, 60); 599894e0ac8Smrg set_color_register(color_registers, 10, 60, 26, 26); 600894e0ac8Smrg set_color_register(color_registers, 11, 33, 60, 33); 601894e0ac8Smrg set_color_register(color_registers, 12, 60, 33, 60); 602894e0ac8Smrg set_color_register(color_registers, 13, 33, 60, 60); 603894e0ac8Smrg set_color_register(color_registers, 14, 60, 60, 33); 604894e0ac8Smrg set_color_register(color_registers, 15, 80, 80, 80); 605894e0ac8Smrg break; 606894e0ac8Smrg case 382: /* FIXME: verify */ 607894e0ac8Smrg set_color_register(color_registers, 0, 0, 0, 0); 608894e0ac8Smrg set_color_register(color_registers, 1, 100, 100, 100); 609e0a2b6dfSmrg break; 610e0a2b6dfSmrg } 611e0a2b6dfSmrg 612894e0ac8Smrg#ifdef DEBUG_PALETTE 613894e0ac8Smrg { 614894e0ac8Smrg unsigned i; 615e0a2b6dfSmrg 616894e0ac8Smrg for (i = 0U; i < MAX_COLOR_REGISTERS; i++) { 61701037d57Smrg TRACE(("initial value for register %03u: %d,%d,%d\n", 618894e0ac8Smrg i, 619894e0ac8Smrg color_registers[i].r, 620894e0ac8Smrg color_registers[i].g, 62101037d57Smrg color_registers[i].b)); 622894e0ac8Smrg } 623894e0ac8Smrg } 624894e0ac8Smrg#endif 625894e0ac8Smrg} 626e0a2b6dfSmrg 627894e0ac8Smrgunsigned 628894e0ac8Smrgget_color_register_count(TScreen const *screen) 629894e0ac8Smrg{ 630894e0ac8Smrg unsigned num_color_registers; 631e0a2b6dfSmrg 632894e0ac8Smrg if (screen->numcolorregisters >= 0) { 633894e0ac8Smrg num_color_registers = (unsigned) screen->numcolorregisters; 634894e0ac8Smrg } else { 635894e0ac8Smrg num_color_registers = 0U; 636894e0ac8Smrg } 637e0a2b6dfSmrg 638894e0ac8Smrg if (num_color_registers > 1U) { 639894e0ac8Smrg if (num_color_registers > MAX_COLOR_REGISTERS) 640894e0ac8Smrg return MAX_COLOR_REGISTERS; 641894e0ac8Smrg return num_color_registers; 642894e0ac8Smrg } 643e0a2b6dfSmrg 644e0a2b6dfSmrg /* 645e0a2b6dfSmrg * color capabilities: 646e0a2b6dfSmrg * VK100/GIGI 1 plane (12x1 pixel attribute blocks) colorspace is 8 fixed colors (black, white, red, green, blue, cyan, yellow, magenta) 647e0a2b6dfSmrg * VT125 2 planes (4 registers) colorspace is (64?) (color), ? (grayscale) 648894e0ac8Smrg * VT240 2 planes (4 registers) colorspace is 4 shades (grayscale) 649e0a2b6dfSmrg * VT241 2 planes (4 registers) colorspace is ? (color), ? shades (grayscale) 650e0a2b6dfSmrg * VT330 2 planes (4 registers) colorspace is 4 shades (grayscale) 651e0a2b6dfSmrg * VT340 4 planes (16 registers) colorspace is r16g16b16 (color), 16 shades (grayscale) 652894e0ac8Smrg * VT382 1 plane (two fixed colors: black and white) FIXME: verify 653e0a2b6dfSmrg * dxterm ? 654e0a2b6dfSmrg */ 655f2e35a3aSmrg switch (screen->graphics_termid) { 656e0a2b6dfSmrg case 125: 657894e0ac8Smrg return 4U; 658e0a2b6dfSmrg case 240: 659894e0ac8Smrg return 4U; 660e0a2b6dfSmrg case 241: 661894e0ac8Smrg return 4U; 662e0a2b6dfSmrg case 330: 663894e0ac8Smrg return 4U; 664e0a2b6dfSmrg case 340: 665894e0ac8Smrg return 16U; 666894e0ac8Smrg case 382: 667894e0ac8Smrg return 2U; 668e0a2b6dfSmrg default: 669894e0ac8Smrg /* unknown graphics model -- might as well be generous */ 670894e0ac8Smrg return MAX_COLOR_REGISTERS; 671e0a2b6dfSmrg } 672894e0ac8Smrg} 673894e0ac8Smrg 674894e0ac8Smrgstatic void 67504b94745Smrginit_graphic(TScreen *screen, 67604b94745Smrg Graphic *graphic, 677894e0ac8Smrg unsigned type, 678894e0ac8Smrg int charrow, 679894e0ac8Smrg int charcol, 68004b94745Smrg unsigned num_color_registers) 681894e0ac8Smrg{ 68204b94745Smrg int private_colors = screen->privatecolorregisters; 68301037d57Smrg const unsigned max_pixels = (unsigned) (graphic->max_width * 68401037d57Smrg graphic->max_height); 685894e0ac8Smrg 6864419d26bSmrg TRACE(("init_graphic %u pixels at %d,%d\n", max_pixels, charrow, charcol)); 687894e0ac8Smrg 6884419d26bSmrg graphic->hidden = False; 6894419d26bSmrg graphic->dirty = True; 6904419d26bSmrg memset(graphic->pixels, COLOR_HOLE & 0xff, max_pixels * sizeof(RegisterNum)); 69104b94745Smrg memset(graphic->color_registers_used, False, sizeof(graphic->color_registers_used)); 692e0a2b6dfSmrg 693e0a2b6dfSmrg /* 694e0a2b6dfSmrg * text and graphics interactions: 695e0a2b6dfSmrg * VK100/GIGI text writes on top of graphics buffer, color attribute shared with text 696e0a2b6dfSmrg * VT240,VT241,VT330,VT340 text writes on top of graphics buffer 697894e0ac8Smrg * VT382 text writes on top of graphics buffer FIXME: verify 698e0a2b6dfSmrg * VT125 graphics buffer overlaid on top of text in B&W display, text not present in color display 699e0a2b6dfSmrg */ 700e0a2b6dfSmrg 701894e0ac8Smrg /* 702894e0ac8Smrg * dimensions (ReGIS logical, physical): 703894e0ac8Smrg * VK100/GIGI 768x4?? 768x240(status?) 704894e0ac8Smrg * VT125 768x460 768x230(+10status) (1:2 aspect ratio, ReGIS halves vertical addresses through "odd y emulation") 705894e0ac8Smrg * VT240 800x460 800x230(+10status) (1:2 aspect ratio, ReGIS halves vertical addresses through "odd y emulation") 706894e0ac8Smrg * VT241 800x460 800x230(+10status) (1:2 aspect ratio, ReGIS halves vertical addresses through "odd y emulation") 707894e0ac8Smrg * VT330 800x480 800x480(+?status) 708894e0ac8Smrg * VT340 800x480 800x480(+?status) 709894e0ac8Smrg * VT382 960x750 sixel only 710894e0ac8Smrg * dxterm ?x? ?x? variable? 711894e0ac8Smrg */ 712e0a2b6dfSmrg 713e0a2b6dfSmrg graphic->actual_width = 0; 714e0a2b6dfSmrg graphic->actual_height = 0; 715e0a2b6dfSmrg 716894e0ac8Smrg graphic->pixw = 1; 717894e0ac8Smrg graphic->pixh = 1; 718894e0ac8Smrg 719894e0ac8Smrg graphic->valid_registers = num_color_registers; 720894e0ac8Smrg TRACE(("%d color registers\n", graphic->valid_registers)); 721894e0ac8Smrg 722e0a2b6dfSmrg graphic->private_colors = private_colors; 723e0a2b6dfSmrg if (graphic->private_colors) { 72404b94745Smrg#if OPT_INHERIT_COLORS 72504b94745Smrg unsigned ii; 72604b94745Smrg int max_charrow = -1; 72704b94745Smrg Graphic *newest = NULL; 72804b94745Smrg#endif 72904b94745Smrg 730894e0ac8Smrg TRACE(("using private color registers\n")); 73104b94745Smrg 73204b94745Smrg#if OPT_INHERIT_COLORS 73304b94745Smrg FOR_EACH_SLOT(ii) { 73404b94745Smrg Graphic *check; 73504b94745Smrg if (!(check = getActiveSlot(ii))) 73604b94745Smrg continue; 73704b94745Smrg if (!newest || check->charrow >= max_charrow) { 73804b94745Smrg max_charrow = check->charrow; 73904b94745Smrg newest = check; 74004b94745Smrg } 74104b94745Smrg } 74204b94745Smrg 74304b94745Smrg if (newest != NULL && newest != graphic) { 74404b94745Smrg copy_color_registers(graphic, newest); 74504b94745Smrg } else { 74604b94745Smrg init_color_registers(screen, graphic->private_color_registers); 74704b94745Smrg } 74804b94745Smrg#else 74904b94745Smrg init_color_registers(screen, graphic->private_color_registers); 75004b94745Smrg#endif 751e0a2b6dfSmrg graphic->color_registers = graphic->private_color_registers; 752e0a2b6dfSmrg } else { 753894e0ac8Smrg TRACE(("using shared color registers\n")); 754894e0ac8Smrg graphic->color_registers = getSharedRegisters(); 755e0a2b6dfSmrg } 756e0a2b6dfSmrg 757894e0ac8Smrg graphic->charrow = charrow; 758894e0ac8Smrg graphic->charcol = charcol; 759894e0ac8Smrg graphic->type = type; 7604419d26bSmrg graphic->valid = False; 761e0a2b6dfSmrg} 762e0a2b6dfSmrg 763894e0ac8SmrgGraphic * 764894e0ac8Smrgget_new_graphic(XtermWidget xw, int charrow, int charcol, unsigned type) 765e0a2b6dfSmrg{ 76604b94745Smrg TScreen *screen = TScreenOf(xw); 7674419d26bSmrg const int bufferid = screen->whichBuf; 768f2e35a3aSmrg Graphic *graphic = NULL; 769894e0ac8Smrg unsigned ii; 770e0a2b6dfSmrg 77104b94745Smrg TRACE(("get_new_graphic %d,%d type %d\n", charrow, charcol, type)); 77204b94745Smrg 773894e0ac8Smrg FOR_EACH_SLOT(ii) { 7745104ee6eSmrg if ((graphic = getInactiveSlot(screen, ii)) != NULL) { 77504b94745Smrg TRACE(("using fresh graphic index %u as id %u\n", 77604b94745Smrg ii, next_graphic_id)); 777e0a2b6dfSmrg break; 778894e0ac8Smrg } 779e0a2b6dfSmrg } 780e0a2b6dfSmrg 781894e0ac8Smrg /* if none are free, recycle the graphic scrolled back the farthest */ 782894e0ac8Smrg if (!graphic) { 783e0a2b6dfSmrg int min_charrow = 0; 784894e0ac8Smrg Graphic *min_graphic = NULL; 78504b94745Smrg if_TRACE(unsigned best_ii = (1 + MAX_GRAPHICS)); 786e0a2b6dfSmrg 787894e0ac8Smrg FOR_EACH_SLOT(ii) { 788894e0ac8Smrg if (!(graphic = getActiveSlot(ii))) 789894e0ac8Smrg continue; 790e0a2b6dfSmrg if (!min_graphic || graphic->charrow < min_charrow) { 79104b94745Smrg if_TRACE(best_ii = ii); 792e0a2b6dfSmrg min_charrow = graphic->charrow; 793e0a2b6dfSmrg min_graphic = graphic; 794e0a2b6dfSmrg } 795e0a2b6dfSmrg } 79604b94745Smrg TRACE(("recycling old graphic index %u as id %u\n", 79704b94745Smrg best_ii, next_graphic_id)); 798e0a2b6dfSmrg graphic = min_graphic; 799e0a2b6dfSmrg } 800e0a2b6dfSmrg 801894e0ac8Smrg if (graphic) { 802894e0ac8Smrg unsigned num_color_registers; 803894e0ac8Smrg num_color_registers = get_color_register_count(screen); 804894e0ac8Smrg graphic->xw = xw; 805894e0ac8Smrg graphic->bufferid = bufferid; 806894e0ac8Smrg graphic->id = next_graphic_id++; 80704b94745Smrg init_graphic(screen, 80804b94745Smrg graphic, 809894e0ac8Smrg type, 810894e0ac8Smrg charrow, 811894e0ac8Smrg charcol, 81204b94745Smrg num_color_registers); 813894e0ac8Smrg } 814e0a2b6dfSmrg return graphic; 815e0a2b6dfSmrg} 816e0a2b6dfSmrg 817894e0ac8SmrgGraphic * 818894e0ac8Smrgget_new_or_matching_graphic(XtermWidget xw, 819894e0ac8Smrg int charrow, 820894e0ac8Smrg int charcol, 821894e0ac8Smrg int actual_width, 822894e0ac8Smrg int actual_height, 823894e0ac8Smrg unsigned type) 824e0a2b6dfSmrg{ 825894e0ac8Smrg TScreen const *screen = TScreenOf(xw); 8264419d26bSmrg const int bufferid = screen->whichBuf; 827894e0ac8Smrg Graphic *graphic; 828894e0ac8Smrg unsigned ii; 829894e0ac8Smrg 830894e0ac8Smrg FOR_EACH_SLOT(ii) { 8312e4f8982Smrg TRACE(("checking slot=%u for graphic at %d,%d %dx%d bufferid=%d type=%u\n", ii, 8322e4f8982Smrg charrow, charcol, 8332e4f8982Smrg actual_width, actual_height, 8342e4f8982Smrg bufferid, type)); 8355104ee6eSmrg if ((graphic = getActiveSlot(ii)) != NULL) { 8362e4f8982Smrg if (graphic->type == type && 8372e4f8982Smrg graphic->bufferid == bufferid && 8382e4f8982Smrg graphic->charrow == charrow && 8392e4f8982Smrg graphic->charcol == charcol && 8402e4f8982Smrg graphic->actual_width == actual_width && 8412e4f8982Smrg graphic->actual_height == actual_height) { 8422e4f8982Smrg TRACE(("found existing graphic slot=%u id=%u\n", ii, graphic->id)); 8432e4f8982Smrg return graphic; 8442e4f8982Smrg } 8452e4f8982Smrg TRACE(("not a match: graphic at %d,%d %dx%d bufferid=%d type=%u\n", 8462e4f8982Smrg graphic->charrow, graphic->charcol, 8472e4f8982Smrg graphic->actual_width, graphic->actual_height, 8482e4f8982Smrg graphic->bufferid, graphic->type)); 849e0a2b6dfSmrg } 850e0a2b6dfSmrg } 851e0a2b6dfSmrg 852894e0ac8Smrg /* if no match get a new graphic */ 8535104ee6eSmrg if ((graphic = get_new_graphic(xw, charrow, charcol, type)) != NULL) { 854894e0ac8Smrg graphic->actual_width = actual_width; 855894e0ac8Smrg graphic->actual_height = actual_height; 8562e4f8982Smrg TRACE(("no match; created graphic at %d,%d %dx%d bufferid=%d type=%u\n", 8572e4f8982Smrg graphic->charrow, graphic->charcol, 8582e4f8982Smrg graphic->actual_width, graphic->actual_height, 8592e4f8982Smrg graphic->bufferid, graphic->type)); 860e0a2b6dfSmrg } 861894e0ac8Smrg return graphic; 862e0a2b6dfSmrg} 863e0a2b6dfSmrg 864a5ae21e4Smrg#define ScaleForXColor(s) (unsigned short) ((unsigned long)(s) * MAX_U_COLOR / CHANNEL_MAX) 86501037d57Smrg 86601037d57Smrgstatic int 86701037d57Smrgsave_allocated_color(const ColorRegister *reg, XtermWidget xw, Pixel *pix) 86801037d57Smrg{ 86901037d57Smrg unsigned const rr = ((unsigned) reg->r * (LOOKUP_WIDTH - 1)) / CHANNEL_MAX; 87001037d57Smrg unsigned const gg = ((unsigned) reg->g * (LOOKUP_WIDTH - 1)) / CHANNEL_MAX; 87101037d57Smrg unsigned const bb = ((unsigned) reg->b * (LOOKUP_WIDTH - 1)) / CHANNEL_MAX; 87201037d57Smrg XColor xcolor; 87301037d57Smrg AllocatedColorRegister *new_color; 87401037d57Smrg 8754419d26bSmrg /* *INDENT-EQLS* */ 87601037d57Smrg xcolor.pixel = 0UL; 8774419d26bSmrg xcolor.red = ScaleForXColor(reg->r); 87801037d57Smrg xcolor.green = ScaleForXColor(reg->g); 8794419d26bSmrg xcolor.blue = ScaleForXColor(reg->b); 88001037d57Smrg xcolor.flags = DoRed | DoGreen | DoBlue; 8814419d26bSmrg 88201037d57Smrg if (!allocateBestRGB(xw, &xcolor)) { 88301037d57Smrg TRACE(("unable to allocate xcolor\n")); 88401037d57Smrg *pix = 0UL; 88501037d57Smrg return 0; 886f2e35a3aSmrg } else { 887f2e35a3aSmrg *pix = xcolor.pixel; 88801037d57Smrg 8895307cd1aSmrg if (!(new_color = TypeMalloc(AllocatedColorRegister))) { 890f2e35a3aSmrg TRACE(("unable to save pixel %lu\n", (unsigned long) *pix)); 891f2e35a3aSmrg return 0; 892f2e35a3aSmrg } else { 893f2e35a3aSmrg new_color->r = reg->r; 894f2e35a3aSmrg new_color->g = reg->g; 895f2e35a3aSmrg new_color->b = reg->b; 896f2e35a3aSmrg new_color->pix = *pix; 897f2e35a3aSmrg new_color->next = allocated_colors[rr][gg][bb]; 89801037d57Smrg 899f2e35a3aSmrg allocated_colors[rr][gg][bb] = new_color; 90001037d57Smrg 901f2e35a3aSmrg return 1; 902f2e35a3aSmrg } 903f2e35a3aSmrg } 90401037d57Smrg} 90501037d57Smrg 906f2e35a3aSmrg/* FIXME: with so many possible colors we need to determine 907f2e35a3aSmrg * when to free them to be nice to PseudoColor displays 908f2e35a3aSmrg */ 90901037d57Smrgstatic Pixel 91001037d57Smrgcolor_register_to_xpixel(const ColorRegister *reg, XtermWidget xw) 91101037d57Smrg{ 912f2e35a3aSmrg Pixel result; 913f2e35a3aSmrg unsigned const rr = ((unsigned) reg->r * (LOOKUP_WIDTH - 1)) / CHANNEL_MAX; 914f2e35a3aSmrg unsigned const gg = ((unsigned) reg->g * (LOOKUP_WIDTH - 1)) / CHANNEL_MAX; 915f2e35a3aSmrg unsigned const bb = ((unsigned) reg->b * (LOOKUP_WIDTH - 1)) / CHANNEL_MAX; 916f2e35a3aSmrg const AllocatedColorRegister *search; 91701037d57Smrg 918f2e35a3aSmrg for (search = allocated_colors[rr][gg][bb]; search; search = search->next) { 919f2e35a3aSmrg if (search->r == reg->r && 920f2e35a3aSmrg search->g == reg->g && 921f2e35a3aSmrg search->b == reg->b) { 922f2e35a3aSmrg return search->pix; 923f2e35a3aSmrg } 924f2e35a3aSmrg } 92501037d57Smrg 926f2e35a3aSmrg save_allocated_color(reg, xw, &result); 927f2e35a3aSmrg return result; 928e0a2b6dfSmrg} 929e0a2b6dfSmrg 930e0a2b6dfSmrgstatic void 931894e0ac8Smrgrefresh_graphic(TScreen const *screen, 932894e0ac8Smrg Graphic const *graphic, 93301037d57Smrg ColorRegister *buffer, 93401037d57Smrg int refresh_x, 93501037d57Smrg int refresh_y, 93601037d57Smrg int refresh_w, 93701037d57Smrg int refresh_h, 93801037d57Smrg int draw_x, 93901037d57Smrg int draw_y, 94001037d57Smrg int draw_w, 94101037d57Smrg int draw_h) 942e0a2b6dfSmrg{ 94301037d57Smrg int const pw = graphic->pixw; 94401037d57Smrg int const ph = graphic->pixh; 94501037d57Smrg int const graph_x = graphic->charcol * FontWidth(screen); 94601037d57Smrg int const graph_y = graphic->charrow * FontHeight(screen); 94701037d57Smrg int const graph_w = graphic->actual_width; 94801037d57Smrg int const graph_h = graphic->actual_height; 94901037d57Smrg int const mw = graphic->max_width; 9504419d26bSmrg 951e0a2b6dfSmrg int r, c; 9524419d26bSmrg int pmy; 95301037d57Smrg RegisterNum regnum; 954e0a2b6dfSmrg 9554419d26bSmrg if_TRACE(int holes = 0); 9564419d26bSmrg if_TRACE(int total = 0); 9574419d26bSmrg if_TRACE(int out_of_range = 0); 9584419d26bSmrg 95901037d57Smrg TRACE(("refreshing graphic %u from %d,%d %dx%d (valid=%d, size=%dx%d, scale=%dx%d max=%dx%d)\n", 96001037d57Smrg graphic->id, 96101037d57Smrg graph_x, graph_y, draw_w, draw_h, 962e0a2b6dfSmrg graphic->valid, 963e0a2b6dfSmrg graphic->actual_width, 964e0a2b6dfSmrg graphic->actual_height, 96501037d57Smrg pw, ph, 966e0a2b6dfSmrg graphic->max_width, 96701037d57Smrg graphic->max_height)); 968e0a2b6dfSmrg 96901037d57Smrg TRACE(("refresh pixmap starts at %d,%d\n", refresh_x, refresh_y)); 970e0a2b6dfSmrg 9714419d26bSmrg for (r = 0, pmy = graph_y; r < graph_h; r++, pmy += ph) { 9724419d26bSmrg int pmx, buffer_y, pixel_y; 973894e0ac8Smrg 97401037d57Smrg if (pmy + ph - 1 < draw_y) 975894e0ac8Smrg continue; 97601037d57Smrg if (pmy > draw_y + draw_h - 1) 97701037d57Smrg break; 978894e0ac8Smrg 9794419d26bSmrg if (pmy < draw_y || 9804419d26bSmrg pmy < refresh_y || 9814419d26bSmrg pmy > refresh_y + refresh_h - 1) { 9824419d26bSmrg if_TRACE(out_of_range++); 983f2e35a3aSmrg continue; 984f2e35a3aSmrg } 9854419d26bSmrg pixel_y = r * mw; 9864419d26bSmrg buffer_y = (pmy - refresh_y) * refresh_w; 987f2e35a3aSmrg 9884419d26bSmrg for (c = 0, pmx = graph_x; c < graph_w; c++, pmx += pw) { 989e0a2b6dfSmrg 99001037d57Smrg if (pmx + pw - 1 < draw_x) 991894e0ac8Smrg continue; 99201037d57Smrg if (pmx > draw_x + draw_w - 1) 99301037d57Smrg break; 994e0a2b6dfSmrg 9954419d26bSmrg if (pmx < draw_x || 9964419d26bSmrg pmx < refresh_x || 9974419d26bSmrg pmx > refresh_x + refresh_w - 1) { 9984419d26bSmrg if_TRACE(out_of_range++); 999f2e35a3aSmrg continue; 1000f2e35a3aSmrg } 1001f2e35a3aSmrg 10024419d26bSmrg if_TRACE(total++); 10034419d26bSmrg regnum = graphic->pixels[pixel_y + c]; 100401037d57Smrg if (regnum == COLOR_HOLE) { 10054419d26bSmrg if_TRACE(holes++); 1006f2e35a3aSmrg } else { 10074419d26bSmrg buffer[buffer_y + (pmx - refresh_x)] = 1008f2e35a3aSmrg graphic->color_registers[regnum]; 1009e0a2b6dfSmrg } 1010e0a2b6dfSmrg } 1011894e0ac8Smrg } 1012e0a2b6dfSmrg 101301037d57Smrg TRACE(("done refreshing graphic: %d of %d refreshed pixels were holes; %d were out of pixmap range\n", 101401037d57Smrg holes, total, out_of_range)); 101501037d57Smrg} 101601037d57Smrg 101704b94745Smrg#define MAX_PCT 100. /* HLS uses this for L, S percentages */ 101804b94745Smrg#define MAX_RGB 100. /* use this rather than 255 */ 101904b94745Smrg 1020e0a2b6dfSmrg/* 102104b94745Smrg * In HLS, H is an angle, in degrees, and L, S are percentages. 1022e0a2b6dfSmrg * Primary color hues: 1023e0a2b6dfSmrg * blue: 0 degrees 1024e0a2b6dfSmrg * red: 120 degrees 1025e0a2b6dfSmrg * green: 240 degrees 1026e0a2b6dfSmrg */ 1027894e0ac8Smrgvoid 1028e0a2b6dfSmrghls2rgb(int h, int l, int s, short *r, short *g, short *b) 1029e0a2b6dfSmrg{ 103004b94745Smrg int hs; 103104b94745Smrg const double lv = l / MAX_PCT; 103204b94745Smrg const double sv = s / MAX_PCT; 1033894e0ac8Smrg double c, x, m, c2; 1034e0a2b6dfSmrg double r1, g1, b1; 1035e0a2b6dfSmrg 103604b94745Smrg h = h - 120; /* Rotate so that blue is at 0 degrees */ 103704b94745Smrg while (h < 0) 103804b94745Smrg h += 360; /* Normalize to 0 to 360, */ 103904b94745Smrg while (h >= 360) 104004b94745Smrg h -= 360; 104104b94745Smrg hs = ((h + 59) / 60) % 6; 104204b94745Smrg 1043e0a2b6dfSmrg if (s == 0) { 1044e0a2b6dfSmrg *r = *g = *b = (short) l; 1045e0a2b6dfSmrg return; 1046e0a2b6dfSmrg } 1047e0a2b6dfSmrg 104801037d57Smrg c2 = (2.0 * lv) - 1.0; 104901037d57Smrg if (c2 < 0.0) 1050894e0ac8Smrg c2 = -c2; 1051894e0ac8Smrg c = (1.0 - c2) * sv; 105201037d57Smrg x = (hs & 1) ? c : 0.0; 1053e0a2b6dfSmrg m = lv - 0.5 * c; 1054e0a2b6dfSmrg 105501037d57Smrg switch (hs) { 1056e0a2b6dfSmrg case 0: 1057e0a2b6dfSmrg r1 = c; 1058e0a2b6dfSmrg g1 = x; 1059e0a2b6dfSmrg b1 = 0.0; 1060e0a2b6dfSmrg break; 1061e0a2b6dfSmrg case 1: 1062e0a2b6dfSmrg r1 = x; 1063e0a2b6dfSmrg g1 = c; 1064e0a2b6dfSmrg b1 = 0.0; 1065e0a2b6dfSmrg break; 1066e0a2b6dfSmrg case 2: 1067e0a2b6dfSmrg r1 = 0.0; 1068e0a2b6dfSmrg g1 = c; 1069e0a2b6dfSmrg b1 = x; 1070e0a2b6dfSmrg break; 1071e0a2b6dfSmrg case 3: 1072e0a2b6dfSmrg r1 = 0.0; 1073e0a2b6dfSmrg g1 = x; 1074e0a2b6dfSmrg b1 = c; 1075e0a2b6dfSmrg break; 1076e0a2b6dfSmrg case 4: 1077e0a2b6dfSmrg r1 = x; 1078e0a2b6dfSmrg g1 = 0.0; 1079e0a2b6dfSmrg b1 = c; 1080e0a2b6dfSmrg break; 1081e0a2b6dfSmrg case 5: 1082e0a2b6dfSmrg r1 = c; 1083e0a2b6dfSmrg g1 = 0.0; 1084e0a2b6dfSmrg b1 = x; 1085e0a2b6dfSmrg break; 1086e0a2b6dfSmrg default: 1087894e0ac8Smrg TRACE(("Bad HLS input: [%d,%d,%d], returning white\n", h, l, s)); 108804b94745Smrg *r = (short) 360; 108904b94745Smrg *g = (short) MAX_PCT; 109004b94745Smrg *b = (short) MAX_PCT; 1091e0a2b6dfSmrg return; 1092e0a2b6dfSmrg } 1093e0a2b6dfSmrg 109404b94745Smrg *r = (short) ((r1 + m) * MAX_PCT + 0.5); 109504b94745Smrg *g = (short) ((g1 + m) * MAX_PCT + 0.5); 109604b94745Smrg *b = (short) ((b1 + m) * MAX_PCT + 0.5); 1097e0a2b6dfSmrg 1098e0a2b6dfSmrg if (*r < 0) 1099e0a2b6dfSmrg *r = 0; 1100e0a2b6dfSmrg else if (*r > 100) 1101e0a2b6dfSmrg *r = 100; 1102e0a2b6dfSmrg if (*g < 0) 1103e0a2b6dfSmrg *g = 0; 1104e0a2b6dfSmrg else if (*g > 100) 1105e0a2b6dfSmrg *g = 100; 1106e0a2b6dfSmrg if (*b < 0) 1107e0a2b6dfSmrg *b = 0; 1108e0a2b6dfSmrg else if (*b > 100) 1109e0a2b6dfSmrg *b = 100; 1110e0a2b6dfSmrg} 1111e0a2b6dfSmrg 111204b94745Smrgvoid 111304b94745Smrgrgb2hls(int r, int g, int b, short *h, short *l, short *s) 111404b94745Smrg{ 111504b94745Smrg const double scaled_r = (r / MAX_RGB); 111604b94745Smrg const double scaled_g = (g / MAX_RGB); 111704b94745Smrg const double scaled_b = (b / MAX_RGB); 111804b94745Smrg 111904b94745Smrg const double min_scale = Min(Min(scaled_r, scaled_g), scaled_b); 112004b94745Smrg const double max_scale = Max(Max(scaled_r, scaled_g), scaled_b); 112104b94745Smrg const double dif_scale = max_scale - min_scale; 112204b94745Smrg 112304b94745Smrg double h_work = 0.; 112404b94745Smrg double s_work = 0.; 112504b94745Smrg double l_work = ((max_scale + min_scale) / 2.); 112604b94745Smrg 112704b94745Smrg if (dif_scale != 0.) { 112804b94745Smrg if (l_work < 0.5f) { 112904b94745Smrg s_work = (dif_scale / (max_scale + min_scale)); 113004b94745Smrg } else { 113104b94745Smrg s_work = (dif_scale / (2. - max_scale - min_scale)); 113204b94745Smrg } 113304b94745Smrg 113404b94745Smrg if (scaled_r == max_scale) { 113504b94745Smrg h_work = (scaled_g - scaled_b) / dif_scale; 113604b94745Smrg } else if (scaled_g == max_scale) { 113704b94745Smrg h_work = 2. + (scaled_b - scaled_r) / dif_scale; 113804b94745Smrg } else if (scaled_b == max_scale) { 113904b94745Smrg h_work = 4. + (scaled_r - scaled_g) / dif_scale; 114004b94745Smrg } 114104b94745Smrg } 114204b94745Smrg 114304b94745Smrg h_work *= 60.; 114404b94745Smrg if (h_work < 0) 114504b94745Smrg h_work += 360.; 114604b94745Smrg 114704b94745Smrg s_work *= MAX_RGB; 114804b94745Smrg l_work *= MAX_RGB; 114904b94745Smrg 115004b94745Smrg *h = (short) h_work; 115104b94745Smrg *s = (short) s_work; 115204b94745Smrg *l = (short) l_work; 115304b94745Smrg} 115404b94745Smrg 1155894e0ac8Smrgvoid 1156894e0ac8Smrgdump_graphic(Graphic const *graphic) 1157e0a2b6dfSmrg{ 1158894e0ac8Smrg#if defined(DUMP_COLORS) || defined(DUMP_BITMAP) 1159894e0ac8Smrg RegisterNum color; 1160894e0ac8Smrg#endif 1161894e0ac8Smrg#ifdef DUMP_BITMAP 1162894e0ac8Smrg int r, c; 1163894e0ac8Smrg ColorRegister const *reg; 1164894e0ac8Smrg#endif 1165e0a2b6dfSmrg 1166894e0ac8Smrg (void) graphic; 1167e0a2b6dfSmrg 1168894e0ac8Smrg TRACE(("graphic stats: id=%u charrow=%d charcol=%d actual_width=%d actual_height=%d pixw=%d pixh=%d\n", 1169894e0ac8Smrg graphic->id, 1170894e0ac8Smrg graphic->charrow, 1171894e0ac8Smrg graphic->charcol, 1172894e0ac8Smrg graphic->actual_width, 1173894e0ac8Smrg graphic->actual_height, 1174894e0ac8Smrg graphic->pixw, 1175894e0ac8Smrg graphic->pixh)); 1176e0a2b6dfSmrg 1177894e0ac8Smrg#ifdef DUMP_COLORS 1178894e0ac8Smrg TRACE(("graphic colors:\n")); 1179894e0ac8Smrg for (color = 0; color < graphic->valid_registers; color++) { 1180894e0ac8Smrg TRACE(("%03u: %d,%d,%d\n", 1181894e0ac8Smrg color, 1182894e0ac8Smrg graphic->color_registers[color].r, 1183894e0ac8Smrg graphic->color_registers[color].g, 1184894e0ac8Smrg graphic->color_registers[color].b)); 1185e0a2b6dfSmrg } 1186e0a2b6dfSmrg#endif 1187e0a2b6dfSmrg 1188894e0ac8Smrg#ifdef DUMP_BITMAP 1189894e0ac8Smrg TRACE(("graphic pixels:\n")); 1190894e0ac8Smrg for (r = 0; r < graphic->actual_height; r++) { 1191894e0ac8Smrg for (c = 0; c < graphic->actual_width; c++) { 1192894e0ac8Smrg color = graphic->pixels[r * graphic->max_width + c]; 1193894e0ac8Smrg if (color == COLOR_HOLE) { 1194894e0ac8Smrg TRACE(("?")); 1195e0a2b6dfSmrg } else { 1196894e0ac8Smrg reg = &graphic->color_registers[color]; 1197894e0ac8Smrg if (reg->r + reg->g + reg->b > 200) { 1198894e0ac8Smrg TRACE(("#")); 1199894e0ac8Smrg } else if (reg->r + reg->g + reg->b > 150) { 1200894e0ac8Smrg TRACE(("%%")); 1201894e0ac8Smrg } else if (reg->r + reg->g + reg->b > 100) { 1202894e0ac8Smrg TRACE((":")); 1203894e0ac8Smrg } else if (reg->r + reg->g + reg->b > 80) { 1204894e0ac8Smrg TRACE((".")); 1205894e0ac8Smrg } else { 1206894e0ac8Smrg TRACE((" ")); 1207e0a2b6dfSmrg } 1208e0a2b6dfSmrg } 1209e0a2b6dfSmrg } 1210894e0ac8Smrg TRACE(("\n")); 1211e0a2b6dfSmrg } 1212e0a2b6dfSmrg 1213894e0ac8Smrg TRACE(("\n")); 1214894e0ac8Smrg#endif 1215e0a2b6dfSmrg} 1216e0a2b6dfSmrg 1217e0a2b6dfSmrg/* Erase the portion of any displayed graphic overlapping with a rectangle 121801037d57Smrg * of the given size and location in pixels relative to the start of the 121901037d57Smrg * graphic. This is used to allow text to "erase" graphics underneath it. 1220e0a2b6dfSmrg */ 1221e0a2b6dfSmrgstatic void 1222894e0ac8Smrgerase_graphic(Graphic *graphic, int x, int y, int w, int h) 1223e0a2b6dfSmrg{ 12244419d26bSmrg const int pw = graphic->pixw; 12254419d26bSmrg const int ph = graphic->pixh; 12264419d26bSmrg const int r_min = y - ph + 1; 12274419d26bSmrg const int r_max = y + h - 1; 12284419d26bSmrg const int c_min = x - pw + 1; 12294419d26bSmrg const int c_max = x + w - 1; 1230e0a2b6dfSmrg 12314419d26bSmrg int r; 12324419d26bSmrg int rbase = 0; 1233e0a2b6dfSmrg 1234894e0ac8Smrg TRACE(("erasing graphic %d,%d %dx%d\n", x, y, w, h)); 1235e0a2b6dfSmrg 1236e0a2b6dfSmrg for (r = 0; r < graphic->actual_height; r++) { 12374419d26bSmrg if (rbase >= r_min 12384419d26bSmrg && rbase <= r_max) { 12394419d26bSmrg int c; 12404419d26bSmrg int cbase = 0; 1241894e0ac8Smrg for (c = 0; c < graphic->actual_width; c++) { 12424419d26bSmrg if (cbase >= c_min 12434419d26bSmrg && cbase <= c_max) { 12444419d26bSmrg const int cell = r * graphic->max_width + c; 12454419d26bSmrg ClrSpixel(graphic, cell); 1246894e0ac8Smrg } 1247894e0ac8Smrg cbase += pw; 1248894e0ac8Smrg } 1249e0a2b6dfSmrg } 1250894e0ac8Smrg rbase += ph; 1251e0a2b6dfSmrg } 1252e0a2b6dfSmrg} 1253e0a2b6dfSmrg 1254e0a2b6dfSmrgstatic int 1255894e0ac8Smrgcompare_graphic_ids(const void *left, const void *right) 1256e0a2b6dfSmrg{ 1257894e0ac8Smrg const Graphic *l = *(const Graphic *const *) left; 1258894e0ac8Smrg const Graphic *r = *(const Graphic *const *) right; 1259e0a2b6dfSmrg 1260e0a2b6dfSmrg if (!l->valid || !r->valid) 1261e0a2b6dfSmrg return 0; 126201037d57Smrg 126301037d57Smrg if (l->bufferid < r->bufferid) 126401037d57Smrg return -1; 126501037d57Smrg else if (l->bufferid > r->bufferid) 126601037d57Smrg return 1; 126701037d57Smrg 1268e0a2b6dfSmrg if (l->id < r->id) 1269e0a2b6dfSmrg return -1; 1270e0a2b6dfSmrg else 1271e0a2b6dfSmrg return 1; 1272e0a2b6dfSmrg} 1273e0a2b6dfSmrg 127401037d57Smrgstatic void 127501037d57Smrgclip_area(int *orig_x, int *orig_y, int *orig_w, int *orig_h, 127601037d57Smrg int clip_x, int clip_y, int clip_w, int clip_h) 1277e0a2b6dfSmrg{ 127801037d57Smrg if (*orig_x < clip_x) { 127901037d57Smrg const int diff = clip_x - *orig_x; 128001037d57Smrg *orig_x += diff; 128101037d57Smrg *orig_w -= diff; 128201037d57Smrg } 128301037d57Smrg if (*orig_w > 0 && *orig_x + *orig_w > clip_x + clip_w) { 128401037d57Smrg *orig_w -= (*orig_x + *orig_w) - (clip_x + clip_w); 128501037d57Smrg } 128601037d57Smrg 128701037d57Smrg if (*orig_y < clip_y) { 128801037d57Smrg const int diff = clip_y - *orig_y; 128901037d57Smrg *orig_y += diff; 129001037d57Smrg *orig_h -= diff; 129101037d57Smrg } 129201037d57Smrg if (*orig_h > 0 && *orig_y + *orig_h > clip_y + clip_h) { 129301037d57Smrg *orig_h -= (*orig_y + *orig_h) - (clip_y + clip_h); 129401037d57Smrg } 129501037d57Smrg} 129601037d57Smrg 12974419d26bSmrgstatic Bool 12984419d26bSmrgGetGraphicsOrder(TScreen *screen, 12994419d26bSmrg int skip_clean, 13004419d26bSmrg Graphic *ordered_graphics[MAX_GRAPHICS], 13014419d26bSmrg unsigned *resultp) 130201037d57Smrg{ 13034419d26bSmrg unsigned ii; 130401037d57Smrg unsigned active_count; 1305e0a2b6dfSmrg 13064419d26bSmrg *resultp = active_count = 0; 1307894e0ac8Smrg FOR_EACH_SLOT(ii) { 130801037d57Smrg Graphic *graphic; 130901037d57Smrg if (!(graphic = getActiveSlot(ii))) 131001037d57Smrg continue; 131101037d57Smrg TRACE(("refreshing graphic %d on buffer %d, current buffer %d\n", 131201037d57Smrg graphic->id, graphic->bufferid, screen->whichBuf)); 131301037d57Smrg if (screen->whichBuf == 0) { 131401037d57Smrg if (graphic->bufferid != 0) { 131501037d57Smrg TRACE(("skipping graphic %d from alt buffer (%d) when drawing screen=%d\n", 131601037d57Smrg graphic->id, graphic->bufferid, screen->whichBuf)); 131701037d57Smrg continue; 131801037d57Smrg } 131901037d57Smrg } else { 132001037d57Smrg if (graphic->bufferid == 0 && graphic->charrow >= 0) { 132101037d57Smrg TRACE(("skipping graphic %d from normal buffer (%d) when drawing screen=%d because it is not in scrollback area\n", 132201037d57Smrg graphic->id, graphic->bufferid, screen->whichBuf)); 132301037d57Smrg continue; 132401037d57Smrg } 132501037d57Smrg if (graphic->bufferid == 1 && 132601037d57Smrg graphic->charrow + (graphic->actual_height + 132701037d57Smrg FontHeight(screen) - 1) / 132801037d57Smrg FontHeight(screen) < 0) { 132901037d57Smrg TRACE(("skipping graphic %d from alt buffer (%d) when drawing screen=%d because it is completely in scrollback area\n", 133001037d57Smrg graphic->id, graphic->bufferid, screen->whichBuf)); 133101037d57Smrg continue; 133201037d57Smrg } 1333894e0ac8Smrg } 13342e4f8982Smrg if (graphic->hidden) 13352e4f8982Smrg continue; 133601037d57Smrg ordered_graphics[active_count++] = graphic; 1337894e0ac8Smrg } 133801037d57Smrg 133901037d57Smrg if (active_count == 0) 13404419d26bSmrg return False; 134101037d57Smrg if (active_count > 1) { 1342894e0ac8Smrg qsort(ordered_graphics, 134301037d57Smrg (size_t) active_count, 1344894e0ac8Smrg sizeof(ordered_graphics[0]), 1345894e0ac8Smrg compare_graphic_ids); 1346e0a2b6dfSmrg } 1347e0a2b6dfSmrg 134801037d57Smrg if (skip_clean) { 13494419d26bSmrg unsigned jj; 135001037d57Smrg unsigned skip_count; 1351e0a2b6dfSmrg 135201037d57Smrg for (jj = 0; jj < active_count; ++jj) { 135301037d57Smrg if (ordered_graphics[jj]->dirty) 135401037d57Smrg break; 1355e0a2b6dfSmrg } 135601037d57Smrg skip_count = jj; 135701037d57Smrg if (skip_count == active_count) 13584419d26bSmrg return False; 1359e0a2b6dfSmrg 136001037d57Smrg active_count -= skip_count; 136101037d57Smrg for (jj = 0; jj < active_count; ++jj) { 136201037d57Smrg ordered_graphics[jj] = ordered_graphics[jj + skip_count]; 136301037d57Smrg } 1364e0a2b6dfSmrg } 13654419d26bSmrg *resultp = active_count; 13664419d26bSmrg return True; 13674419d26bSmrg} 1368e0a2b6dfSmrg 13694419d26bSmrgstatic ColorRegister * 13704419d26bSmrgAllocGraphicsBuffer(TScreen *screen, 13714419d26bSmrg int ncols, 13724419d26bSmrg int nrows) 13734419d26bSmrg{ 13744419d26bSmrg int xx, yy; 13754419d26bSmrg int const refresh_w = ncols * FontWidth(screen); 13764419d26bSmrg int const refresh_h = nrows * FontHeight(screen); 13774419d26bSmrg ColorRegister *buffer; 13784419d26bSmrg 13795307cd1aSmrg if (!(buffer = TypeMallocN(ColorRegister, 13805307cd1aSmrg (unsigned) refresh_w * (unsigned) refresh_h))) { 138101037d57Smrg TRACE(("unable to allocate %dx%d buffer for graphics refresh\n", 138201037d57Smrg refresh_w, refresh_h)); 13834419d26bSmrg } else { 13844419d26bSmrg /* assuming two's complement, the memset will be much faster than loop */ 13854419d26bSmrg if ((unsigned short) null_color.r == 0xffff) { 13864419d26bSmrg memset(buffer, 0xff, 13874419d26bSmrg sizeof(ColorRegister) * (size_t) (refresh_h * refresh_w)); 13884419d26bSmrg } else { 13894419d26bSmrg for (yy = 0; yy < refresh_h; yy++) { 13904419d26bSmrg for (xx = 0; xx < refresh_w; xx++) { 13914419d26bSmrg buffer[yy * refresh_w + xx] = null_color; 13924419d26bSmrg } 13934419d26bSmrg } 139401037d57Smrg } 139501037d57Smrg } 13964419d26bSmrg return buffer; 13974419d26bSmrg} 1398e0a2b6dfSmrg 13994419d26bSmrgtypedef struct { 14004419d26bSmrg int x_min; 14014419d26bSmrg int x_max; 14024419d26bSmrg int y_min; 14034419d26bSmrg int y_max; 14044419d26bSmrg} ClipLimits; 14054419d26bSmrg 14064419d26bSmrgstatic Boolean 14074419d26bSmrgRefreshClipped(TScreen *screen, 14084419d26bSmrg int leftcol, 14094419d26bSmrg int toprow, 14104419d26bSmrg int ncols, 14114419d26bSmrg int nrows, 14124419d26bSmrg Graphic *ordered_graphics[MAX_GRAPHICS], 14134419d26bSmrg unsigned active_count, 14144419d26bSmrg ColorRegister *buffer, 14154419d26bSmrg ClipLimits * result) 14164419d26bSmrg{ 14174419d26bSmrg int const scroll_y = screen->topline * FontHeight(screen); 14184419d26bSmrg int const refresh_x = leftcol * FontWidth(screen); 14194419d26bSmrg int const refresh_y = toprow * FontHeight(screen) + scroll_y; 14204419d26bSmrg int const refresh_w = ncols * FontWidth(screen); 14214419d26bSmrg int const refresh_h = nrows * FontHeight(screen); 14224419d26bSmrg ClipLimits my_limits; 14234419d26bSmrg unsigned jj; 14244419d26bSmrg 14254419d26bSmrg int const altarea_x = 0; 14264419d26bSmrg int const altarea_y = 0; 14274419d26bSmrg int const altarea_w = Width(screen) * FontWidth(screen); 14284419d26bSmrg int const altarea_h = Height(screen) * FontHeight(screen); 14294419d26bSmrg 14304419d26bSmrg int const scrollarea_x = 0; 14314419d26bSmrg int const scrollarea_y = scroll_y; 14324419d26bSmrg int const scrollarea_w = Width(screen) * FontWidth(screen); 14334419d26bSmrg int const scrollarea_h = -scroll_y; 14344419d26bSmrg 14354419d26bSmrg int const mainarea_x = 0; 14364419d26bSmrg int const mainarea_y = scroll_y; 14374419d26bSmrg int const mainarea_w = Width(screen) * FontWidth(screen); 14384419d26bSmrg int const mainarea_h = -scroll_y + Height(screen) * FontHeight(screen); 14394419d26bSmrg 14404419d26bSmrg my_limits.x_min = refresh_x + refresh_w; 14414419d26bSmrg my_limits.x_max = refresh_x - 1; 14424419d26bSmrg my_limits.y_min = refresh_y + refresh_h; 14434419d26bSmrg my_limits.y_max = refresh_y - 1; 14444419d26bSmrg for (jj = 0; jj < active_count; ++jj) { 14454419d26bSmrg Graphic *graphic = ordered_graphics[jj]; 14464419d26bSmrg int draw_x = graphic->charcol * FontWidth(screen); 14474419d26bSmrg int draw_y = graphic->charrow * FontHeight(screen); 14484419d26bSmrg int draw_w = graphic->actual_width; 14494419d26bSmrg int draw_h = graphic->actual_height; 14504419d26bSmrg 14514419d26bSmrg if (screen->whichBuf != 0) { 14524419d26bSmrg if (graphic->bufferid != 0) { 14534419d26bSmrg /* clip to alt buffer */ 14544419d26bSmrg clip_area(&draw_x, &draw_y, &draw_w, &draw_h, 14554419d26bSmrg altarea_x, altarea_y, altarea_w, altarea_h); 145601037d57Smrg } else { 14574419d26bSmrg /* clip to scrollback area */ 145801037d57Smrg clip_area(&draw_x, &draw_y, &draw_w, &draw_h, 14594419d26bSmrg scrollarea_x, scrollarea_y, 14604419d26bSmrg scrollarea_w, scrollarea_h); 146101037d57Smrg } 14624419d26bSmrg } else { 14634419d26bSmrg /* clip to scrollback + normal area */ 146401037d57Smrg clip_area(&draw_x, &draw_y, &draw_w, &draw_h, 14654419d26bSmrg mainarea_x, mainarea_y, 14664419d26bSmrg mainarea_w, mainarea_h); 14674419d26bSmrg } 14684419d26bSmrg 14694419d26bSmrg clip_area(&draw_x, &draw_y, &draw_w, &draw_h, 14704419d26bSmrg refresh_x, refresh_y, refresh_w, refresh_h); 14714419d26bSmrg 14724419d26bSmrg TRACE(("refresh: graph=%u\n", jj)); 14734419d26bSmrg TRACE((" refresh_x=%d refresh_y=%d refresh_w=%d refresh_h=%d\n", 14744419d26bSmrg refresh_x, refresh_y, refresh_w, refresh_h)); 14754419d26bSmrg TRACE((" draw_x=%d draw_y=%d draw_w=%d draw_h=%d\n", 14764419d26bSmrg draw_x, draw_y, draw_w, draw_h)); 14774419d26bSmrg 14784419d26bSmrg if (draw_w > 0 && draw_h > 0) { 14794419d26bSmrg refresh_graphic(screen, graphic, buffer, 14804419d26bSmrg refresh_x, refresh_y, 14814419d26bSmrg refresh_w, refresh_h, 14824419d26bSmrg draw_x, draw_y, 14834419d26bSmrg draw_w, draw_h); 14844419d26bSmrg if (draw_x < my_limits.x_min) 14854419d26bSmrg my_limits.x_min = draw_x; 14864419d26bSmrg if (draw_x + draw_w - 1 > my_limits.x_max) 14874419d26bSmrg my_limits.x_max = draw_x + draw_w - 1; 14884419d26bSmrg if (draw_y < my_limits.y_min) 14894419d26bSmrg my_limits.y_min = draw_y; 14904419d26bSmrg if (draw_y + draw_h - 1 > my_limits.y_max) 14914419d26bSmrg my_limits.y_max = draw_y + draw_h - 1; 1492e0a2b6dfSmrg } 14934419d26bSmrg graphic->dirty = False; 149401037d57Smrg } 1495e0a2b6dfSmrg 14964419d26bSmrg if (my_limits.x_max < refresh_x || 14974419d26bSmrg my_limits.x_min > refresh_x + refresh_w - 1 || 14984419d26bSmrg my_limits.y_max < refresh_y || 14994419d26bSmrg my_limits.y_min > refresh_y + refresh_h - 1) { 15004419d26bSmrg return False; 1501e0a2b6dfSmrg } 15024419d26bSmrg *result = my_limits; 15034419d26bSmrg return True; 15044419d26bSmrg} 150501037d57Smrg 15064419d26bSmrgstatic Boolean 15074419d26bSmrgFindGraphicHoles(int refresh_x, 15084419d26bSmrg int refresh_y, 15094419d26bSmrg int refresh_w, 15104419d26bSmrg ColorRegister *buffer, 15114419d26bSmrg ClipLimits * limits, 15124419d26bSmrg unsigned *result) 15134419d26bSmrg{ 15144419d26bSmrg const int y_min = limits->y_min - refresh_y; 15154419d26bSmrg const int y_max = limits->y_max - refresh_y; 15164419d26bSmrg const int x_min = limits->x_min - refresh_x; 15174419d26bSmrg const int x_max = limits->x_max - refresh_x; 15184419d26bSmrg const ColorRegister *base = buffer + (y_min * refresh_w); 15194419d26bSmrg int xx, yy; 15204419d26bSmrg 15214419d26bSmrg unsigned holes = 0U; 15224419d26bSmrg unsigned non_holes = 0U; 15234419d26bSmrg 15244419d26bSmrg for (yy = y_min; yy <= y_max; yy++) { 15254419d26bSmrg const ColorRegister *scan = base + x_min; 15264419d26bSmrg for (xx = x_min; xx <= x_max; xx++) { 15274419d26bSmrg if (scan->r < 0 || scan->g < 0 || scan->b < 0) { 15284419d26bSmrg holes++; 15294419d26bSmrg } else { 15304419d26bSmrg non_holes++; 153101037d57Smrg } 15324419d26bSmrg if (non_holes && holes) 15334419d26bSmrg goto finish; 15344419d26bSmrg ++scan; 153501037d57Smrg } 15364419d26bSmrg base += refresh_w; 153701037d57Smrg } 153801037d57Smrg 15394419d26bSmrg finish: 15404419d26bSmrg *result = holes; 15414419d26bSmrg return (non_holes != 0); 15424419d26bSmrg} 15434419d26bSmrg 15444419d26bSmrg/* the coordinates are relative to the screen */ 15454419d26bSmrgstatic void 15464419d26bSmrgrefresh_graphics(XtermWidget xw, 15474419d26bSmrg int leftcol, 15484419d26bSmrg int toprow, 15494419d26bSmrg int ncols, 15504419d26bSmrg int nrows, 15514419d26bSmrg int skip_clean) 15524419d26bSmrg{ 15534419d26bSmrg TScreen *const screen = TScreenOf(xw); 15544419d26bSmrg Display *const display = screen->display; 15554419d26bSmrg Window const drawable = VDrawable(screen); 15564419d26bSmrg int const scroll_y = screen->topline * FontHeight(screen); 15574419d26bSmrg int const refresh_x = leftcol * FontWidth(screen); 15584419d26bSmrg int const refresh_y = toprow * FontHeight(screen) + scroll_y; 15594419d26bSmrg int const refresh_w = ncols * FontWidth(screen); 15604419d26bSmrg 15614419d26bSmrg Graphic *ordered_graphics[MAX_GRAPHICS]; 15624419d26bSmrg unsigned active_count; 15634419d26bSmrg unsigned holes; 15644419d26bSmrg int xx, yy; 15654419d26bSmrg 15664419d26bSmrg ColorRegister *buffer; 15674419d26bSmrg ClipLimits clip_limits; 15684419d26bSmrg 15694419d26bSmrg if_TRACE(int const refresh_h = nrows * FontHeight(screen)); 15704419d26bSmrg 15714419d26bSmrg if (!GetGraphicsOrder(screen, skip_clean, ordered_graphics, &active_count)) 15724419d26bSmrg return; 15734419d26bSmrg 15744419d26bSmrg if (!valid_graphics) { 15754419d26bSmrg memset(&xgcv, 0, sizeof(xgcv)); 15764419d26bSmrg xgcv.graphics_exposures = False; 15774419d26bSmrg graphics_gc = XCreateGC(display, drawable, GCGraphicsExposures, &xgcv); 15784419d26bSmrg last_color = null_color; 15794419d26bSmrg gc_color = null_color; 15805104ee6eSmrg if (graphics_gc == NULL) { 15814419d26bSmrg TRACE(("unable to allocate GC for graphics refresh\n")); 15824419d26bSmrg valid_graphics = -1; 15834419d26bSmrg } else { 15844419d26bSmrg valid_graphics = 1; 15854419d26bSmrg } 15864419d26bSmrg } 15874419d26bSmrg if (valid_graphics < 0) 15884419d26bSmrg return; 15894419d26bSmrg 15904419d26bSmrg if ((buffer = AllocGraphicsBuffer(screen, ncols, nrows)) == NULL) 15914419d26bSmrg return; 15924419d26bSmrg 15934419d26bSmrg TRACE(("refresh: screen->topline=%d leftcol=%d toprow=%d nrows=%d ncols=%d (%d,%d %dx%d)\n", 15944419d26bSmrg screen->topline, 15954419d26bSmrg leftcol, toprow, 15964419d26bSmrg nrows, ncols, 15974419d26bSmrg refresh_x, refresh_y, 15984419d26bSmrg refresh_w, refresh_h)); 15994419d26bSmrg 16004419d26bSmrg if (!RefreshClipped(screen, leftcol, toprow, ncols, nrows, 16014419d26bSmrg ordered_graphics, 16024419d26bSmrg active_count, 16034419d26bSmrg buffer, 16044419d26bSmrg &clip_limits)) { 16054419d26bSmrg free(buffer); 16064419d26bSmrg return; 16074419d26bSmrg } 16084419d26bSmrg 16094419d26bSmrg if (!FindGraphicHoles(refresh_x, 16104419d26bSmrg refresh_y, 16114419d26bSmrg refresh_w, 16124419d26bSmrg buffer, 16134419d26bSmrg &clip_limits, 16144419d26bSmrg &holes)) { 161501037d57Smrg TRACE(("refresh: visible graphics areas are erased; nothing to do\n")); 161601037d57Smrg free(buffer); 161701037d57Smrg return; 161801037d57Smrg } 161901037d57Smrg 162001037d57Smrg /* 162101037d57Smrg * If we have any holes we can't just copy an image rectangle, and masking 162201037d57Smrg * with bitmaps is very expensive. This fallback is surprisingly faster 162301037d57Smrg * than the XPutImage version in some cases, but I don't know why. 162401037d57Smrg * (This is even though there's no X11 primitive for drawing a horizontal 162501037d57Smrg * line of height one and no attempt is made to handle multiple lines at 162601037d57Smrg * once.) 162701037d57Smrg */ 162801037d57Smrg if (holes > 0U) { 162901037d57Smrg int run; 163001037d57Smrg 163101037d57Smrg run = 0; 16324419d26bSmrg for (yy = clip_limits.y_min - refresh_y; 16334419d26bSmrg yy <= clip_limits.y_max - refresh_y; 16344419d26bSmrg yy++) { 16354419d26bSmrg for (xx = clip_limits.x_min - refresh_x; 16364419d26bSmrg xx <= clip_limits.x_max - refresh_x; 163701037d57Smrg xx++) { 163801037d57Smrg const ColorRegister color = buffer[yy * refresh_w + xx]; 163901037d57Smrg 164001037d57Smrg if (color.r < 0 || color.g < 0 || color.b < 0) { 164101037d57Smrg last_color = color; 164201037d57Smrg if (run > 0) { 164301037d57Smrg XDrawLine(display, drawable, graphics_gc, 164401037d57Smrg OriginX(screen) + refresh_x + xx - run, 164501037d57Smrg (OriginY(screen) - scroll_y) + refresh_y + yy, 164601037d57Smrg OriginX(screen) + refresh_x + xx - 1, 164701037d57Smrg (OriginY(screen) - scroll_y) + refresh_y + yy); 164801037d57Smrg run = 0; 164901037d57Smrg } 165001037d57Smrg continue; 165101037d57Smrg } 165201037d57Smrg 1653f2e35a3aSmrg if (DiffColor(color, last_color)) { 165401037d57Smrg last_color = color; 165501037d57Smrg if (run > 0) { 165601037d57Smrg XDrawLine(display, drawable, graphics_gc, 165701037d57Smrg OriginX(screen) + refresh_x + xx - run, 165801037d57Smrg (OriginY(screen) - scroll_y) + refresh_y + yy, 165901037d57Smrg OriginX(screen) + refresh_x + xx - 1, 166001037d57Smrg (OriginY(screen) - scroll_y) + refresh_y + yy); 166101037d57Smrg run = 0; 166201037d57Smrg } 166301037d57Smrg 1664f2e35a3aSmrg if (DiffColor(color, gc_color)) { 166501037d57Smrg xgcv.foreground = 166601037d57Smrg color_register_to_xpixel(&color, xw); 166701037d57Smrg XChangeGC(display, graphics_gc, GCForeground, &xgcv); 166801037d57Smrg gc_color = color; 166901037d57Smrg } 167001037d57Smrg } 167101037d57Smrg run++; 167201037d57Smrg } 167301037d57Smrg if (run > 0) { 1674f2e35a3aSmrg last_color = null_color; 167501037d57Smrg XDrawLine(display, drawable, graphics_gc, 167601037d57Smrg OriginX(screen) + refresh_x + xx - run, 167701037d57Smrg (OriginY(screen) - scroll_y) + refresh_y + yy, 167801037d57Smrg OriginX(screen) + refresh_x + xx - 1, 167901037d57Smrg (OriginY(screen) - scroll_y) + refresh_y + yy); 168001037d57Smrg run = 0; 168101037d57Smrg } 168201037d57Smrg } 168301037d57Smrg } else { 1684f2e35a3aSmrg ColorRegister old_colors[2]; 1685f2e35a3aSmrg Pixel fg, old_result[2]; 168601037d57Smrg XImage *image; 168701037d57Smrg char *imgdata; 16884419d26bSmrg const unsigned image_w = ((unsigned) clip_limits.x_max + 1U - 16894419d26bSmrg (unsigned) clip_limits.x_min); 16904419d26bSmrg const unsigned image_h = ((unsigned) clip_limits.y_max + 1U - 16914419d26bSmrg (unsigned) clip_limits.y_min); 1692f2e35a3aSmrg int nn; 169301037d57Smrg 169401037d57Smrg image = XCreateImage(display, xw->visInfo->visual, 169501037d57Smrg (unsigned) xw->visInfo->depth, 169601037d57Smrg ZPixmap, 0, NULL, 169701037d57Smrg image_w, image_h, 1698f2e35a3aSmrg (int) (sizeof(int) * 8U), 0); 169901037d57Smrg if (!image) { 170001037d57Smrg TRACE(("unable to allocate XImage for graphics refresh\n")); 170101037d57Smrg free(buffer); 170201037d57Smrg return; 170301037d57Smrg } 17045307cd1aSmrg imgdata = TypeMallocN(char, (size_t)(image_h * (unsigned)image->bytes_per_line)); 170501037d57Smrg if (!imgdata) { 170601037d57Smrg TRACE(("unable to allocate XImage for graphics refresh\n")); 170701037d57Smrg XDestroyImage(image); 170801037d57Smrg free(buffer); 170901037d57Smrg return; 171001037d57Smrg } 171101037d57Smrg image->data = imgdata; 171201037d57Smrg 171301037d57Smrg fg = 0U; 1714f2e35a3aSmrg nn = 0; 1715f2e35a3aSmrg 1716f2e35a3aSmrg /* two-level cache cuts down on lookup-calls */ 1717f2e35a3aSmrg old_result[0] = 0U; 1718f2e35a3aSmrg old_result[1] = 0U; 1719f2e35a3aSmrg old_colors[0] = null_color; 1720f2e35a3aSmrg old_colors[1] = null_color; 1721f2e35a3aSmrg 17224419d26bSmrg for (yy = clip_limits.y_min - refresh_y; 17234419d26bSmrg yy <= clip_limits.y_max - refresh_y; 17244419d26bSmrg yy++) { 17254419d26bSmrg for (xx = clip_limits.x_min - refresh_x; 17264419d26bSmrg xx <= clip_limits.x_max - refresh_x; 172701037d57Smrg xx++) { 172801037d57Smrg const ColorRegister color = buffer[yy * refresh_w + xx]; 172901037d57Smrg 1730f2e35a3aSmrg if (DiffColor(color, old_colors[nn])) { 1731f2e35a3aSmrg if (DiffColor(color, old_colors[!nn])) { 1732f2e35a3aSmrg nn = !nn; 1733f2e35a3aSmrg fg = color_register_to_xpixel(&color, xw); 1734f2e35a3aSmrg old_result[nn] = fg; 1735f2e35a3aSmrg old_colors[nn] = color; 1736f2e35a3aSmrg } else { 1737f2e35a3aSmrg nn = !nn; 1738f2e35a3aSmrg fg = old_result[nn]; 1739f2e35a3aSmrg } 174001037d57Smrg } 174101037d57Smrg 1742f2e35a3aSmrg XPutPixel(image, 17434419d26bSmrg xx + refresh_x - clip_limits.x_min, 17444419d26bSmrg yy + refresh_y - clip_limits.y_min, fg); 174501037d57Smrg } 174601037d57Smrg } 174701037d57Smrg 174801037d57Smrg XPutImage(display, drawable, graphics_gc, image, 174901037d57Smrg 0, 0, 17504419d26bSmrg OriginX(screen) + clip_limits.x_min, 17514419d26bSmrg (OriginY(screen) - scroll_y) + clip_limits.y_min, 175201037d57Smrg image_w, image_h); 175301037d57Smrg free(imgdata); 175401037d57Smrg image->data = NULL; 175501037d57Smrg XDestroyImage(image); 175601037d57Smrg } 175701037d57Smrg 175801037d57Smrg free(buffer); 175901037d57Smrg XFlush(display); 176001037d57Smrg} 176101037d57Smrg 176201037d57Smrgvoid 176301037d57Smrgrefresh_displayed_graphics(XtermWidget xw, 176401037d57Smrg int leftcol, 176501037d57Smrg int toprow, 176601037d57Smrg int ncols, 176701037d57Smrg int nrows) 176801037d57Smrg{ 176901037d57Smrg refresh_graphics(xw, leftcol, toprow, ncols, nrows, 0); 177001037d57Smrg} 177101037d57Smrg 177201037d57Smrgvoid 177301037d57Smrgrefresh_modified_displayed_graphics(XtermWidget xw) 177401037d57Smrg{ 177501037d57Smrg TScreen const *screen = TScreenOf(xw); 177601037d57Smrg refresh_graphics(xw, 0, 0, MaxCols(screen), MaxRows(screen), 1); 1777e0a2b6dfSmrg} 1778e0a2b6dfSmrg 1779894e0ac8Smrgvoid 178001037d57Smrgscroll_displayed_graphics(XtermWidget xw, int rows) 1781e0a2b6dfSmrg{ 1782f2e35a3aSmrg if (used_graphics) { 1783f2e35a3aSmrg TScreen const *screen = TScreenOf(xw); 1784f2e35a3aSmrg unsigned ii; 1785e0a2b6dfSmrg 1786f2e35a3aSmrg TRACE(("graphics scroll: moving all up %d rows\n", rows)); 1787f2e35a3aSmrg /* FIXME: VT125 ReGIS graphics are fixed at the upper left of the display; need to verify */ 1788894e0ac8Smrg 1789f2e35a3aSmrg FOR_EACH_SLOT(ii) { 1790f2e35a3aSmrg Graphic *graphic; 17912e4f8982Smrg 1792f2e35a3aSmrg if (!(graphic = getActiveSlot(ii))) 1793f2e35a3aSmrg continue; 1794f2e35a3aSmrg if (graphic->bufferid != screen->whichBuf) 1795f2e35a3aSmrg continue; 1796f2e35a3aSmrg if (graphic->hidden) 1797f2e35a3aSmrg continue; 1798e0a2b6dfSmrg 1799f2e35a3aSmrg graphic->charrow -= rows; 1800f2e35a3aSmrg } 1801e0a2b6dfSmrg } 1802e0a2b6dfSmrg} 1803e0a2b6dfSmrg 1804894e0ac8Smrgvoid 1805e0a2b6dfSmrgpixelarea_clear_displayed_graphics(TScreen const *screen, 1806e0a2b6dfSmrg int winx, 1807e0a2b6dfSmrg int winy, 1808e0a2b6dfSmrg int w, 1809e0a2b6dfSmrg int h) 1810e0a2b6dfSmrg{ 1811894e0ac8Smrg unsigned ii; 1812e0a2b6dfSmrg 1813f2e35a3aSmrg if (!used_graphics) 1814f2e35a3aSmrg return; 1815f2e35a3aSmrg 1816894e0ac8Smrg FOR_EACH_SLOT(ii) { 181701037d57Smrg Graphic *graphic; 181801037d57Smrg /* FIXME: are these coordinates (scrolled) screen-relative? */ 181901037d57Smrg int const scroll_y = (screen->whichBuf == 0 182001037d57Smrg ? screen->topline * FontHeight(screen) 182101037d57Smrg : 0); 182201037d57Smrg int graph_x; 182301037d57Smrg int graph_y; 182401037d57Smrg int x, y; 182501037d57Smrg 1826894e0ac8Smrg if (!(graphic = getActiveSlot(ii))) 1827e0a2b6dfSmrg continue; 182801037d57Smrg if (graphic->bufferid != screen->whichBuf) 182901037d57Smrg continue; 18302e4f8982Smrg if (graphic->hidden) 18312e4f8982Smrg continue; 1832e0a2b6dfSmrg 183301037d57Smrg graph_x = graphic->charcol * FontWidth(screen); 183401037d57Smrg graph_y = graphic->charrow * FontHeight(screen); 183501037d57Smrg x = winx - graph_x; 183601037d57Smrg y = (winy - scroll_y) - graph_y; 1837e0a2b6dfSmrg 183801037d57Smrg TRACE(("pixelarea clear graphics: screen->topline=%d winx=%d winy=%d w=%d h=%d x=%d y=%d\n", 1839e0a2b6dfSmrg screen->topline, 1840e0a2b6dfSmrg winx, winy, 1841e0a2b6dfSmrg w, h, 1842e0a2b6dfSmrg x, y)); 1843894e0ac8Smrg erase_graphic(graphic, x, y, w, h); 1844e0a2b6dfSmrg } 1845e0a2b6dfSmrg} 1846e0a2b6dfSmrg 1847894e0ac8Smrgvoid 1848e0a2b6dfSmrgchararea_clear_displayed_graphics(TScreen const *screen, 1849e0a2b6dfSmrg int leftcol, 1850e0a2b6dfSmrg int toprow, 1851e0a2b6dfSmrg int ncols, 1852e0a2b6dfSmrg int nrows) 1853e0a2b6dfSmrg{ 1854f2e35a3aSmrg if (used_graphics) { 1855f2e35a3aSmrg int const x = leftcol * FontWidth(screen); 1856f2e35a3aSmrg int const y = toprow * FontHeight(screen); 1857f2e35a3aSmrg int const w = ncols * FontWidth(screen); 1858f2e35a3aSmrg int const h = nrows * FontHeight(screen); 1859e0a2b6dfSmrg 1860f2e35a3aSmrg TRACE(("chararea clear graphics: screen->topline=%d leftcol=%d toprow=%d nrows=%d ncols=%d x=%d y=%d w=%d h=%d\n", 1861f2e35a3aSmrg screen->topline, 1862f2e35a3aSmrg leftcol, toprow, 1863f2e35a3aSmrg nrows, ncols, 1864f2e35a3aSmrg x, y, w, h)); 1865f2e35a3aSmrg pixelarea_clear_displayed_graphics(screen, x, y, w, h); 1866f2e35a3aSmrg } 1867e0a2b6dfSmrg} 1868e0a2b6dfSmrg 1869894e0ac8Smrgvoid 1870e0a2b6dfSmrgreset_displayed_graphics(TScreen const *screen) 1871e0a2b6dfSmrg{ 187204b94745Smrg init_color_registers(screen, getSharedRegisters()); 1873894e0ac8Smrg 1874f2e35a3aSmrg if (used_graphics) { 1875f2e35a3aSmrg unsigned ii; 1876894e0ac8Smrg 1877f2e35a3aSmrg TRACE(("resetting all graphics\n")); 1878f2e35a3aSmrg FOR_EACH_SLOT(ii) { 1879f2e35a3aSmrg deactivateSlot(ii); 1880f2e35a3aSmrg } 188104b94745Smrg#if OPT_REGIS_GRAPHICS 188204b94745Smrg reset_regis(); 188304b94745Smrg#endif 1884894e0ac8Smrg } 1885894e0ac8Smrg} 1886894e0ac8Smrg 1887894e0ac8Smrg#ifdef NO_LEAKS 1888894e0ac8Smrgvoid 18894419d26bSmrgnoleaks_graphics(Display *dpy) 1890894e0ac8Smrg{ 1891894e0ac8Smrg unsigned ii; 1892894e0ac8Smrg 1893894e0ac8Smrg FOR_EACH_SLOT(ii) { 1894894e0ac8Smrg deactivateSlot(ii); 1895e0a2b6dfSmrg } 18964419d26bSmrg if (valid_graphics > 0) 18974419d26bSmrg XFreeGC(dpy, graphics_gc); 1898e0a2b6dfSmrg} 1899894e0ac8Smrg#endif 1900