graphics.c revision f2e35a3a
1f2e35a3aSmrg/* $XTermId: graphics.c,v 1.92 2020/10/12 17:58:12 Walter.Harms Exp $ */ 2e0a2b6dfSmrg 3e0a2b6dfSmrg/* 4f2e35a3aSmrg * Copyright 2013-2019,2020 by Ross Combs 5f2e35a3aSmrg * Copyright 2013-2019,2020 by Thomas E. Dickey 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 46894e0ac8Smrg#undef DUMP_BITMAP 47894e0ac8Smrg#undef DUMP_COLORS 48894e0ac8Smrg#undef DEBUG_PALETTE 49894e0ac8Smrg#undef DEBUG_PIXEL 50e0a2b6dfSmrg#undef DEBUG_REFRESH 51e0a2b6dfSmrg 5201037d57Smrg/* 5301037d57Smrg * graphics TODO list 5401037d57Smrg * 55e0a2b6dfSmrg * ReGIS: 562e4f8982Smrg * - ship a default alphabet zero font instead of scaling Xft fonts 57913cc679Smrg * - input cursors 58913cc679Smrg * - output cursors 59913cc679Smrg * - mouse/tablet/arrow-key input 602e4f8982Smrg * - fix graphic pages for ReGIS -- they should also apply to text and sixel graphics 6101037d57Smrg * - fix interpolated curves to more closely match implementation (identical despite direction and starting point) 6201037d57Smrg * - non-ASCII alphabets 632e4f8982Smrg * - enter/leave anywhere in a command 642e4f8982Smrg * - locator key definitions (DECLKD) 65894e0ac8Smrg * - command display mode 66913cc679Smrg * - re-rasterization on window resize 67894e0ac8Smrg * - macros 6801037d57Smrg * - improved fills for narrow angles (track actual lines not just pixels) 69913cc679Smrg * - hardcopy/screen-capture support (need dialog of some sort for safety) 70913cc679Smrg * - error reporting 7101037d57Smrg * 72e0a2b6dfSmrg * sixel: 73894e0ac8Smrg * - fix problem where new_row < 0 during sixel parsing (see FIXME) 7401037d57Smrg * - screen-capture support (need dialog of some sort for safety) 7501037d57Smrg * 76894e0ac8Smrg * VT55/VT105 waveform graphics 77894e0ac8Smrg * - everything 7801037d57Smrg * 7901037d57Smrg * Tektronix: 8001037d57Smrg * - color (VT340 4014 emulation, 41xx, IRAF GTERM, and also MS-DOS Kermit color support) 8101037d57Smrg * - polygon fill (41xx) 8201037d57Smrg * - clear area extension 8301037d57Smrg * - area fill extension 8401037d57Smrg * - pixel operations (RU/RS/RP) 8501037d57Smrg * - research other 41xx and 42xx extensions 8601037d57Smrg * 8701037d57Smrg * common graphics features: 88894e0ac8Smrg * - handle light/dark screen modes (CSI?5[hl]) 89894e0ac8Smrg * - update text fg/bg color which overlaps images 9001037d57Smrg * - handle graphic updates in scroll regions (verify effect on graphics) 91894e0ac8Smrg * - handle rectangular area copies (verify they work with graphics) 9201037d57Smrg * - invalidate graphics under graphic if same origin, at least as big, and bg not transparent 9301037d57Smrg * - invalidate graphic if completely scrolled past end of scrollback 9401037d57Smrg * - invalidate graphic if all pixels are transparent/erased 9501037d57Smrg * - invalidate graphic if completely scrolled out of alt buffer 9601037d57Smrg * - posturize requested colors to match hardware palettes (e.g. only four possible shades on VT240) 97894e0ac8Smrg * - color register report/restore 9801037d57Smrg * - ability to select/copy graphics for pasting in other programs 9901037d57Smrg * - ability to show non-scroll-mode sixel graphics in a separate window 10001037d57Smrg * - ability to show ReGIS graphics in a separate window 10101037d57Smrg * - ability to show Tektronix graphics in VT100 window 1022e4f8982Smrg * - truncate graphics at bottom edge of terminal? 1032e4f8982Smrg * - locator events (DECEFR DECSLE DECELR DECLRP) 1042e4f8982Smrg * - locator controller mode (CSI6i / CSI7i) 10501037d57Smrg * 10601037d57Smrg * new escape sequences: 10701037d57Smrg * - way to query text font size without "window ops" (or make "window ops" permissions more fine grained) 108894e0ac8Smrg * - way to query and set the number of graphics pages 10901037d57Smrg * 110894e0ac8Smrg * ReGIS extensions: 11101037d57Smrg * - non-integer text scaling 112913cc679Smrg * - free distortionless text rotation (vs. simulating the distortion and aligning to 45deg increments) 11301037d57Smrg * - font characteristics: bold/underline/italic 11401037d57Smrg * - remove/increase arbitrary limits (pattern size, pages, alphabets, stack size, font names, etc.) 11501037d57Smrg * - shade/fill with borders 11601037d57Smrg * - sprites (copy portion of page into/out of buffer with scaling and rotation) 11701037d57Smrg * - ellipses 11801037d57Smrg * - 2D patterns 11901037d57Smrg * - option to set actual graphic size (not just coordinate range) 12001037d57Smrg * - gradients (for lines and fills) 121894e0ac8Smrg * - line width (RLogin has this and it is mentioned in docs for the DEC ReGIS to Postscript converter) 122894e0ac8Smrg * - transparency 123894e0ac8Smrg * - background color as stackable write control 124894e0ac8Smrg * - true color (virtual color registers created upon lookup) 125894e0ac8Smrg * - anti-aliasing 126913cc679Smrg * - variable-width (proportional) text 127e0a2b6dfSmrg */ 128e0a2b6dfSmrg 129e0a2b6dfSmrg/* font sizes: 130e0a2b6dfSmrg * VT510: 131e0a2b6dfSmrg * 80 Columns 132 Columns Maximum Number of Lines 132e0a2b6dfSmrg * 10 x 16 6 x 16 26 lines + keyboard indicator line 133894e0ac8Smrg * 10 x 13 6 x 13 26 lines + keyboard indicator line 134e0a2b6dfSmrg * 10 x 10 6 x 10 42 lines + keyboard indicator line 135e0a2b6dfSmrg * 10 x 8 6 x 8 53 lines + keyboard indicator line 13601037d57Smrg */ 13701037d57Smrg 13801037d57Smrgtypedef struct allocated_color_register { 13901037d57Smrg struct allocated_color_register *next; 14001037d57Smrg Pixel pix; 14101037d57Smrg short r, g, b; 14201037d57Smrg} AllocatedColorRegister; 14301037d57Smrg 14401037d57Smrg#define LOOKUP_WIDTH 16 14501037d57Smrgstatic AllocatedColorRegister *allocated_colors[LOOKUP_WIDTH][LOOKUP_WIDTH][LOOKUP_WIDTH]; 146e0a2b6dfSmrg 147894e0ac8Smrg#define FOR_EACH_SLOT(ii) for (ii = 0U; ii < MAX_GRAPHICS; ii++) 148894e0ac8Smrg 149894e0ac8Smrgstatic ColorRegister *shared_color_registers; 150894e0ac8Smrgstatic Graphic *displayed_graphics[MAX_GRAPHICS]; 151894e0ac8Smrgstatic unsigned next_graphic_id = 0U; 152f2e35a3aSmrgstatic unsigned used_graphics; /* 0 to MAX_GRAPHICS */ 153f2e35a3aSmrg 154f2e35a3aSmrg#define DiffColor(this,that) \ 155f2e35a3aSmrg (this.r != that.r || \ 156f2e35a3aSmrg this.g != that.g || \ 157f2e35a3aSmrg this.b != that.b) 158f2e35a3aSmrg 159f2e35a3aSmrgstatic ColorRegister null_color = 160f2e35a3aSmrg{-1, -1, -1}; 161894e0ac8Smrg 162894e0ac8Smrgstatic ColorRegister * 163894e0ac8SmrgallocRegisters(void) 164e0a2b6dfSmrg{ 165894e0ac8Smrg return TypeCallocN(ColorRegister, MAX_COLOR_REGISTERS); 166894e0ac8Smrg} 167894e0ac8Smrg 168894e0ac8Smrgstatic Graphic * 169894e0ac8SmrgfreeGraphic(Graphic *obj) 170894e0ac8Smrg{ 171894e0ac8Smrg if (obj) { 172f2e35a3aSmrg free(obj->pixels); 173f2e35a3aSmrg free(obj->private_color_registers); 174894e0ac8Smrg free(obj); 175894e0ac8Smrg } 176894e0ac8Smrg return NULL; 177894e0ac8Smrg} 178894e0ac8Smrg 179894e0ac8Smrgstatic Graphic * 18001037d57SmrgallocGraphic(int max_w, int max_h) 181894e0ac8Smrg{ 182894e0ac8Smrg Graphic *result = TypeCalloc(Graphic); 183894e0ac8Smrg if (result) { 18401037d57Smrg result->max_width = max_w; 18501037d57Smrg result->max_height = max_h; 18601037d57Smrg if (!(result->pixels = TypeCallocN(RegisterNum, 18701037d57Smrg (size_t) max_w * (size_t) max_h))) { 188894e0ac8Smrg result = freeGraphic(result); 189894e0ac8Smrg } else if (!(result->private_color_registers = allocRegisters())) { 190894e0ac8Smrg result = freeGraphic(result); 191e0a2b6dfSmrg } 192e0a2b6dfSmrg } 193894e0ac8Smrg return result; 194894e0ac8Smrg} 195e0a2b6dfSmrg 196f2e35a3aSmrg#define getActiveSlot(n) \ 197f2e35a3aSmrg (((n) < MAX_GRAPHICS && \ 198f2e35a3aSmrg displayed_graphics[n] && \ 199f2e35a3aSmrg displayed_graphics[n]->valid) \ 200f2e35a3aSmrg ? displayed_graphics[n] \ 201f2e35a3aSmrg : NULL) 202e0a2b6dfSmrg 203894e0ac8Smrgstatic Graphic * 20401037d57SmrggetInactiveSlot(const TScreen *screen, unsigned n) 205894e0ac8Smrg{ 206894e0ac8Smrg if (n < MAX_GRAPHICS && 207894e0ac8Smrg (!displayed_graphics[n] || 208894e0ac8Smrg !displayed_graphics[n]->valid)) { 209894e0ac8Smrg if (!displayed_graphics[n]) { 21001037d57Smrg displayed_graphics[n] = allocGraphic(screen->graphics_max_wide, 21101037d57Smrg screen->graphics_max_high); 212f2e35a3aSmrg used_graphics += (displayed_graphics[n] != NULL); 213894e0ac8Smrg } 214894e0ac8Smrg return displayed_graphics[n]; 215894e0ac8Smrg } 216894e0ac8Smrg return NULL; 217894e0ac8Smrg} 218894e0ac8Smrg 219894e0ac8Smrgstatic ColorRegister * 220894e0ac8SmrggetSharedRegisters(void) 221894e0ac8Smrg{ 222894e0ac8Smrg if (!shared_color_registers) 223894e0ac8Smrg shared_color_registers = allocRegisters(); 224894e0ac8Smrg return shared_color_registers; 225894e0ac8Smrg} 226e0a2b6dfSmrg 227e0a2b6dfSmrgstatic void 228894e0ac8SmrgdeactivateSlot(unsigned n) 229e0a2b6dfSmrg{ 230f2e35a3aSmrg if ((n < MAX_GRAPHICS) && displayed_graphics[n]) { 231894e0ac8Smrg displayed_graphics[n] = freeGraphic(displayed_graphics[n]); 232f2e35a3aSmrg used_graphics--; 233894e0ac8Smrg } 234894e0ac8Smrg} 235e0a2b6dfSmrg 236894e0ac8Smrgextern RegisterNum 237894e0ac8Smrgread_pixel(Graphic *graphic, int x, int y) 238894e0ac8Smrg{ 23901037d57Smrg if (x < 0 || x >= graphic->actual_width || 24001037d57Smrg y < 0 || y >= graphic->actual_height) { 241894e0ac8Smrg return COLOR_HOLE; 242e0a2b6dfSmrg } 243894e0ac8Smrg 244894e0ac8Smrg return graphic->pixels[y * graphic->max_width + x]; 245e0a2b6dfSmrg} 246e0a2b6dfSmrg 24701037d57Smrg#define _draw_pixel(G, X, Y, C) \ 24801037d57Smrg do { \ 24901037d57Smrg (G)->pixels[(Y) * (G)->max_width + (X)] = (RegisterNum) (C); \ 25001037d57Smrg } while (0) 25101037d57Smrg 252894e0ac8Smrgvoid 253894e0ac8Smrgdraw_solid_pixel(Graphic *graphic, int x, int y, unsigned color) 254e0a2b6dfSmrg{ 255894e0ac8Smrg assert(color <= MAX_COLOR_REGISTERS); 256e0a2b6dfSmrg 257894e0ac8Smrg#ifdef DEBUG_PIXEL 258894e0ac8Smrg TRACE(("drawing pixel at %d,%d color=%hu (hole=%hu, [%d,%d,%d])\n", 259894e0ac8Smrg x, 260894e0ac8Smrg y, 261e0a2b6dfSmrg color, 262894e0ac8Smrg COLOR_HOLE, 263e0a2b6dfSmrg ((color != COLOR_HOLE) 264894e0ac8Smrg ? (unsigned) graphic->color_registers[color].r : 0U), 265e0a2b6dfSmrg ((color != COLOR_HOLE) 266894e0ac8Smrg ? (unsigned) graphic->color_registers[color].g : 0U), 267e0a2b6dfSmrg ((color != COLOR_HOLE) 268894e0ac8Smrg ? (unsigned) graphic->color_registers[color].b : 0U))); 269894e0ac8Smrg#endif 270894e0ac8Smrg if (x >= 0 && x < graphic->actual_width && 271894e0ac8Smrg y >= 0 && y < graphic->actual_height) { 27201037d57Smrg _draw_pixel(graphic, x, y, color); 273894e0ac8Smrg if (color < MAX_COLOR_REGISTERS) 274894e0ac8Smrg graphic->color_registers_used[color] = 1; 275894e0ac8Smrg } 276894e0ac8Smrg} 277894e0ac8Smrg 278894e0ac8Smrgvoid 279894e0ac8Smrgdraw_solid_rectangle(Graphic *graphic, int x1, int y1, int x2, int y2, unsigned color) 280894e0ac8Smrg{ 281894e0ac8Smrg int x, y; 282894e0ac8Smrg int tmp; 283894e0ac8Smrg 284894e0ac8Smrg assert(color <= MAX_COLOR_REGISTERS); 285894e0ac8Smrg 286894e0ac8Smrg if (x1 > x2) { 287894e0ac8Smrg EXCHANGE(x1, x2, tmp); 288894e0ac8Smrg } 289894e0ac8Smrg if (y1 > y2) { 290894e0ac8Smrg EXCHANGE(y1, y2, tmp); 291894e0ac8Smrg } 292894e0ac8Smrg 29301037d57Smrg if (x2 < 0 || x1 >= graphic->actual_width || 29401037d57Smrg y2 < 0 || y1 >= graphic->actual_height) 29501037d57Smrg return; 29601037d57Smrg 29701037d57Smrg if (x1 < 0) 29801037d57Smrg x1 = 0; 29901037d57Smrg if (x2 >= graphic->actual_width) 30001037d57Smrg x2 = graphic->actual_width - 1; 30101037d57Smrg if (y1 < 0) 30201037d57Smrg y1 = 0; 30301037d57Smrg if (y2 >= graphic->actual_height) 30401037d57Smrg y2 = graphic->actual_height - 1; 30501037d57Smrg 30601037d57Smrg if (color < MAX_COLOR_REGISTERS) 30701037d57Smrg graphic->color_registers_used[color] = 1; 308894e0ac8Smrg for (y = y1; y <= y2; y++) 30901037d57Smrg for (x = x1; x <= x2; x++) 31001037d57Smrg _draw_pixel(graphic, x, y, color); 311894e0ac8Smrg} 312894e0ac8Smrg 313f2e35a3aSmrg#if 0 /* unused */ 314894e0ac8Smrgvoid 315894e0ac8Smrgdraw_solid_line(Graphic *graphic, int x1, int y1, int x2, int y2, unsigned color) 316894e0ac8Smrg{ 317894e0ac8Smrg int x, y; 318894e0ac8Smrg int dx, dy; 319894e0ac8Smrg int dir, diff; 320894e0ac8Smrg 321894e0ac8Smrg assert(color <= MAX_COLOR_REGISTERS); 322894e0ac8Smrg 323894e0ac8Smrg dx = abs(x1 - x2); 324894e0ac8Smrg dy = abs(y1 - y2); 325894e0ac8Smrg 326894e0ac8Smrg if (dx > dy) { 327894e0ac8Smrg if (x1 > x2) { 328894e0ac8Smrg int tmp; 329894e0ac8Smrg EXCHANGE(x1, x2, tmp); 330894e0ac8Smrg EXCHANGE(y1, y2, tmp); 331894e0ac8Smrg } 332894e0ac8Smrg if (y1 < y2) 333894e0ac8Smrg dir = 1; 334894e0ac8Smrg else if (y1 > y2) 335894e0ac8Smrg dir = -1; 336894e0ac8Smrg else 337894e0ac8Smrg dir = 0; 338894e0ac8Smrg 339894e0ac8Smrg diff = 0; 340894e0ac8Smrg y = y1; 341894e0ac8Smrg for (x = x1; x <= x2; x++) { 342894e0ac8Smrg if (diff >= dx) { 343894e0ac8Smrg diff -= dx; 344894e0ac8Smrg y += dir; 345e0a2b6dfSmrg } 346894e0ac8Smrg diff += dy; 347894e0ac8Smrg draw_solid_pixel(graphic, x, y, color); 348894e0ac8Smrg } 349894e0ac8Smrg } else { 350894e0ac8Smrg if (y1 > y2) { 351894e0ac8Smrg int tmp; 352894e0ac8Smrg EXCHANGE(x1, x2, tmp); 353894e0ac8Smrg EXCHANGE(y1, y2, tmp); 354894e0ac8Smrg } 355894e0ac8Smrg if (x1 < x2) 356894e0ac8Smrg dir = 1; 357894e0ac8Smrg else if (x1 > x2) 358894e0ac8Smrg dir = -1; 359894e0ac8Smrg else 360894e0ac8Smrg dir = 0; 361894e0ac8Smrg 362894e0ac8Smrg diff = 0; 363894e0ac8Smrg x = x1; 364894e0ac8Smrg for (y = y1; y <= y2; y++) { 365894e0ac8Smrg if (diff >= dy) { 366894e0ac8Smrg diff -= dy; 367894e0ac8Smrg x += dir; 368894e0ac8Smrg } 369894e0ac8Smrg diff += dx; 370894e0ac8Smrg draw_solid_pixel(graphic, x, y, color); 371e0a2b6dfSmrg } 372e0a2b6dfSmrg } 373e0a2b6dfSmrg} 374f2e35a3aSmrg#endif 375e0a2b6dfSmrg 37601037d57Smrgvoid 37701037d57Smrgcopy_overlapping_area(Graphic *graphic, int src_ul_x, int src_ul_y, 37801037d57Smrg int dst_ul_x, int dst_ul_y, unsigned w, unsigned h, 37901037d57Smrg unsigned default_color) 38001037d57Smrg{ 38101037d57Smrg int sx, ex, dx; 38201037d57Smrg int sy, ey, dy; 38301037d57Smrg int xx, yy; 38401037d57Smrg RegisterNum color; 38501037d57Smrg 38601037d57Smrg if (dst_ul_x <= src_ul_x) { 38701037d57Smrg sx = 0; 38801037d57Smrg ex = (int) w - 1; 38901037d57Smrg dx = +1; 39001037d57Smrg } else { 39101037d57Smrg sx = (int) w - 1; 39201037d57Smrg ex = 0; 39301037d57Smrg dx = -1; 39401037d57Smrg } 39501037d57Smrg 39601037d57Smrg if (dst_ul_y <= src_ul_y) { 39701037d57Smrg sy = 0; 39801037d57Smrg ey = (int) h - 1; 39901037d57Smrg dy = +1; 40001037d57Smrg } else { 40101037d57Smrg sy = (int) h - 1; 40201037d57Smrg ey = 0; 40301037d57Smrg dy = -1; 40401037d57Smrg } 40501037d57Smrg 40601037d57Smrg for (yy = sy; yy != ey + dy; yy += dy) { 4072e4f8982Smrg int dst_y = dst_ul_y + yy; 4082e4f8982Smrg int src_y = src_ul_y + yy; 40901037d57Smrg if (dst_y < 0 || dst_y >= (int) graphic->actual_height) 41001037d57Smrg continue; 41101037d57Smrg 41201037d57Smrg for (xx = sx; xx != ex + dx; xx += dx) { 4132e4f8982Smrg int dst_x = dst_ul_x + xx; 4142e4f8982Smrg int src_x = src_ul_x + xx; 41501037d57Smrg if (dst_x < 0 || dst_x >= (int) graphic->actual_width) 41601037d57Smrg continue; 41701037d57Smrg 41801037d57Smrg if (src_x < 0 || src_x >= (int) graphic->actual_width || 41901037d57Smrg src_y < 0 || src_y >= (int) graphic->actual_height) 42001037d57Smrg color = (RegisterNum) default_color; 42101037d57Smrg else 42201037d57Smrg color = graphic->pixels[(unsigned) (src_y * 42301037d57Smrg graphic->max_width) + 42401037d57Smrg (unsigned) src_x]; 42501037d57Smrg 42601037d57Smrg graphic->pixels[(unsigned) (dst_y * graphic->max_width) + 42701037d57Smrg (unsigned) dst_x] = color; 42801037d57Smrg } 42901037d57Smrg } 43001037d57Smrg} 43101037d57Smrg 432e0a2b6dfSmrgstatic void 433894e0ac8Smrgset_color_register(ColorRegister *color_registers, 434894e0ac8Smrg unsigned color, 435894e0ac8Smrg int r, 436894e0ac8Smrg int g, 437894e0ac8Smrg int b) 438e0a2b6dfSmrg{ 439e0a2b6dfSmrg ColorRegister *reg = &color_registers[color]; 440e0a2b6dfSmrg reg->r = (short) r; 441e0a2b6dfSmrg reg->g = (short) g; 442e0a2b6dfSmrg reg->b = (short) b; 443e0a2b6dfSmrg} 444e0a2b6dfSmrg 445894e0ac8Smrg/* Graphics which don't use private colors will act as if they are using a 446894e0ac8Smrg * device-wide color palette. 447894e0ac8Smrg */ 448894e0ac8Smrgstatic void 449894e0ac8Smrgset_shared_color_register(unsigned color, int r, int g, int b) 450894e0ac8Smrg{ 451894e0ac8Smrg unsigned ii; 452894e0ac8Smrg 453894e0ac8Smrg assert(color < MAX_COLOR_REGISTERS); 454894e0ac8Smrg 455894e0ac8Smrg set_color_register(getSharedRegisters(), color, r, g, b); 456894e0ac8Smrg 457f2e35a3aSmrg if (!used_graphics) 458f2e35a3aSmrg return; 459f2e35a3aSmrg 460894e0ac8Smrg FOR_EACH_SLOT(ii) { 4612e4f8982Smrg Graphic *graphic; 4622e4f8982Smrg 463894e0ac8Smrg if (!(graphic = getActiveSlot(ii))) 464894e0ac8Smrg continue; 465894e0ac8Smrg if (graphic->private_colors) 466894e0ac8Smrg continue; 467894e0ac8Smrg 468894e0ac8Smrg if (graphic->color_registers_used[ii]) { 469894e0ac8Smrg graphic->dirty = 1; 470894e0ac8Smrg } 471894e0ac8Smrg } 472894e0ac8Smrg} 473894e0ac8Smrg 474894e0ac8Smrgvoid 475894e0ac8Smrgupdate_color_register(Graphic *graphic, 476894e0ac8Smrg unsigned color, 477894e0ac8Smrg int r, 478894e0ac8Smrg int g, 479894e0ac8Smrg int b) 480894e0ac8Smrg{ 481894e0ac8Smrg assert(color < MAX_COLOR_REGISTERS); 482894e0ac8Smrg 483894e0ac8Smrg if (graphic->private_colors) { 484894e0ac8Smrg set_color_register(graphic->private_color_registers, 485894e0ac8Smrg color, r, g, b); 486894e0ac8Smrg if (graphic->color_registers_used[color]) { 487894e0ac8Smrg graphic->dirty = 1; 488894e0ac8Smrg } 489894e0ac8Smrg graphic->color_registers_used[color] = 1; 490894e0ac8Smrg } else { 491894e0ac8Smrg set_shared_color_register(color, r, g, b); 492894e0ac8Smrg } 493894e0ac8Smrg} 494894e0ac8Smrg 495894e0ac8Smrg#define SQUARE(X) ( (X) * (X) ) 496894e0ac8Smrg 497894e0ac8SmrgRegisterNum 498894e0ac8Smrgfind_color_register(ColorRegister const *color_registers, int r, int g, int b) 499894e0ac8Smrg{ 500894e0ac8Smrg unsigned i; 501894e0ac8Smrg unsigned closest_index; 502894e0ac8Smrg unsigned closest_distance; 503894e0ac8Smrg 504894e0ac8Smrg /* I have no idea what algorithm DEC used for this. 505894e0ac8Smrg * The documentation warns that it is unpredictable, especially with values 506894e0ac8Smrg * far away from any allocated color so it is probably a very simple 50701037d57Smrg * heuristic rather than something fancy like finding the minimum distance 508894e0ac8Smrg * in a linear perceptive color space. 509894e0ac8Smrg */ 510894e0ac8Smrg closest_index = MAX_COLOR_REGISTERS; 511894e0ac8Smrg closest_distance = 0U; 512894e0ac8Smrg for (i = 0U; i < MAX_COLOR_REGISTERS; i++) { 5132e4f8982Smrg unsigned d = (unsigned) (SQUARE(2 * (color_registers[i].r - r)) + 5142e4f8982Smrg SQUARE(3 * (color_registers[i].g - g)) + 5152e4f8982Smrg SQUARE(1 * (color_registers[i].b - b))); 516894e0ac8Smrg if (closest_index == MAX_COLOR_REGISTERS || d < closest_distance) { 517894e0ac8Smrg closest_index = i; 518894e0ac8Smrg closest_distance = d; 519894e0ac8Smrg } 520894e0ac8Smrg } 521894e0ac8Smrg 522894e0ac8Smrg TRACE(("found closest color register to %d,%d,%d: %u (distance %u value %d,%d,%d)\n", 523894e0ac8Smrg r, g, b, 524894e0ac8Smrg closest_index, 525894e0ac8Smrg closest_distance, 526894e0ac8Smrg color_registers[closest_index].r, 527894e0ac8Smrg color_registers[closest_index].g, 528894e0ac8Smrg color_registers[closest_index].b)); 529894e0ac8Smrg return (RegisterNum) closest_index; 530894e0ac8Smrg} 531894e0ac8Smrg 532e0a2b6dfSmrgstatic void 533f2e35a3aSmrginit_color_registers(ColorRegister *color_registers, int graphics_termid) 534e0a2b6dfSmrg{ 535f2e35a3aSmrg TRACE(("setting initial colors for terminal %d\n", graphics_termid)); 536e0a2b6dfSmrg { 537894e0ac8Smrg unsigned i; 538e0a2b6dfSmrg 539e0a2b6dfSmrg for (i = 0U; i < MAX_COLOR_REGISTERS; i++) { 540894e0ac8Smrg set_color_register(color_registers, (RegisterNum) i, 0, 0, 0); 541e0a2b6dfSmrg } 542e0a2b6dfSmrg } 543e0a2b6dfSmrg 544e0a2b6dfSmrg /* 545e0a2b6dfSmrg * default color registers: 546e0a2b6dfSmrg * (mono) (color) 547e0a2b6dfSmrg * VK100/GIGI (fixed) 548e0a2b6dfSmrg * VT125: 549e0a2b6dfSmrg * 0: 0% 0% 550e0a2b6dfSmrg * 1: 33% blue 551e0a2b6dfSmrg * 2: 66% red 552e0a2b6dfSmrg * 3: 100% green 553e0a2b6dfSmrg * VT240: 554e0a2b6dfSmrg * 0: 0% 0% 555e0a2b6dfSmrg * 1: 33% blue 556e0a2b6dfSmrg * 2: 66% red 557e0a2b6dfSmrg * 3: 100% green 558e0a2b6dfSmrg * VT241: 559e0a2b6dfSmrg * 0: 0% 0% 560e0a2b6dfSmrg * 1: 33% blue 561e0a2b6dfSmrg * 2: 66% red 562e0a2b6dfSmrg * 3: 100% green 563e0a2b6dfSmrg * VT330: 564e0a2b6dfSmrg * 0: 0% 0% (bg for light on dark mode) 565e0a2b6dfSmrg * 1: 33% blue (red?) 566e0a2b6dfSmrg * 2: 66% red (green?) 567e0a2b6dfSmrg * 3: 100% green (yellow?) (fg for light on dark mode) 568e0a2b6dfSmrg * VT340: 569e0a2b6dfSmrg * 0: 0% 0% (bg for light on dark mode) 570e0a2b6dfSmrg * 1: 14% blue 571e0a2b6dfSmrg * 2: 29% red 572e0a2b6dfSmrg * 3: 43% green 573e0a2b6dfSmrg * 4: 57% magenta 574e0a2b6dfSmrg * 5: 71% cyan 575e0a2b6dfSmrg * 6: 86% yellow 576e0a2b6dfSmrg * 7: 100% 50% (fg for light on dark mode) 577e0a2b6dfSmrg * 8: 0% 25% 578e0a2b6dfSmrg * 9: 14% gray-blue 579e0a2b6dfSmrg * 10: 29% gray-red 580e0a2b6dfSmrg * 11: 43% gray-green 581e0a2b6dfSmrg * 12: 57% gray-magenta 582e0a2b6dfSmrg * 13: 71% gray-cyan 583e0a2b6dfSmrg * 14: 86% gray-yellow 584894e0ac8Smrg * 15: 100% 75% ("white") 585894e0ac8Smrg * VT382: 586894e0ac8Smrg * ? (FIXME: B&W only?) 587e0a2b6dfSmrg * dxterm: 588e0a2b6dfSmrg * ? 589e0a2b6dfSmrg */ 590f2e35a3aSmrg switch (graphics_termid) { 591e0a2b6dfSmrg case 125: 592e0a2b6dfSmrg case 241: 593894e0ac8Smrg set_color_register(color_registers, 0, 0, 0, 0); 594894e0ac8Smrg set_color_register(color_registers, 1, 0, 0, 100); 595894e0ac8Smrg set_color_register(color_registers, 2, 0, 100, 0); 596894e0ac8Smrg set_color_register(color_registers, 3, 100, 0, 0); 597e0a2b6dfSmrg break; 598e0a2b6dfSmrg case 240: 599e0a2b6dfSmrg case 330: 600894e0ac8Smrg set_color_register(color_registers, 0, 0, 0, 0); 601894e0ac8Smrg set_color_register(color_registers, 1, 33, 33, 33); 602894e0ac8Smrg set_color_register(color_registers, 2, 66, 66, 66); 603894e0ac8Smrg set_color_register(color_registers, 3, 100, 100, 100); 604e0a2b6dfSmrg break; 605e0a2b6dfSmrg case 340: 606e0a2b6dfSmrg default: 607894e0ac8Smrg set_color_register(color_registers, 0, 0, 0, 0); 608894e0ac8Smrg set_color_register(color_registers, 1, 20, 20, 80); 609894e0ac8Smrg set_color_register(color_registers, 2, 80, 13, 13); 610894e0ac8Smrg set_color_register(color_registers, 3, 20, 80, 20); 611894e0ac8Smrg set_color_register(color_registers, 4, 80, 20, 80); 612894e0ac8Smrg set_color_register(color_registers, 5, 20, 80, 80); 613894e0ac8Smrg set_color_register(color_registers, 6, 80, 80, 20); 614894e0ac8Smrg set_color_register(color_registers, 7, 53, 53, 53); 615894e0ac8Smrg set_color_register(color_registers, 8, 26, 26, 26); 616894e0ac8Smrg set_color_register(color_registers, 9, 33, 33, 60); 617894e0ac8Smrg set_color_register(color_registers, 10, 60, 26, 26); 618894e0ac8Smrg set_color_register(color_registers, 11, 33, 60, 33); 619894e0ac8Smrg set_color_register(color_registers, 12, 60, 33, 60); 620894e0ac8Smrg set_color_register(color_registers, 13, 33, 60, 60); 621894e0ac8Smrg set_color_register(color_registers, 14, 60, 60, 33); 622894e0ac8Smrg set_color_register(color_registers, 15, 80, 80, 80); 623894e0ac8Smrg break; 624894e0ac8Smrg case 382: /* FIXME: verify */ 625894e0ac8Smrg set_color_register(color_registers, 0, 0, 0, 0); 626894e0ac8Smrg set_color_register(color_registers, 1, 100, 100, 100); 627e0a2b6dfSmrg break; 628e0a2b6dfSmrg } 629e0a2b6dfSmrg 630894e0ac8Smrg#ifdef DEBUG_PALETTE 631894e0ac8Smrg { 632894e0ac8Smrg unsigned i; 633e0a2b6dfSmrg 634894e0ac8Smrg for (i = 0U; i < MAX_COLOR_REGISTERS; i++) { 63501037d57Smrg TRACE(("initial value for register %03u: %d,%d,%d\n", 636894e0ac8Smrg i, 637894e0ac8Smrg color_registers[i].r, 638894e0ac8Smrg color_registers[i].g, 63901037d57Smrg color_registers[i].b)); 640894e0ac8Smrg } 641894e0ac8Smrg } 642894e0ac8Smrg#endif 643894e0ac8Smrg} 644e0a2b6dfSmrg 645894e0ac8Smrgunsigned 646894e0ac8Smrgget_color_register_count(TScreen const *screen) 647894e0ac8Smrg{ 648894e0ac8Smrg unsigned num_color_registers; 649e0a2b6dfSmrg 650894e0ac8Smrg if (screen->numcolorregisters >= 0) { 651894e0ac8Smrg num_color_registers = (unsigned) screen->numcolorregisters; 652894e0ac8Smrg } else { 653894e0ac8Smrg num_color_registers = 0U; 654894e0ac8Smrg } 655e0a2b6dfSmrg 656894e0ac8Smrg if (num_color_registers > 1U) { 657894e0ac8Smrg if (num_color_registers > MAX_COLOR_REGISTERS) 658894e0ac8Smrg return MAX_COLOR_REGISTERS; 659894e0ac8Smrg return num_color_registers; 660894e0ac8Smrg } 661e0a2b6dfSmrg 662e0a2b6dfSmrg /* 663e0a2b6dfSmrg * color capabilities: 664e0a2b6dfSmrg * VK100/GIGI 1 plane (12x1 pixel attribute blocks) colorspace is 8 fixed colors (black, white, red, green, blue, cyan, yellow, magenta) 665e0a2b6dfSmrg * VT125 2 planes (4 registers) colorspace is (64?) (color), ? (grayscale) 666894e0ac8Smrg * VT240 2 planes (4 registers) colorspace is 4 shades (grayscale) 667e0a2b6dfSmrg * VT241 2 planes (4 registers) colorspace is ? (color), ? shades (grayscale) 668e0a2b6dfSmrg * VT330 2 planes (4 registers) colorspace is 4 shades (grayscale) 669e0a2b6dfSmrg * VT340 4 planes (16 registers) colorspace is r16g16b16 (color), 16 shades (grayscale) 670894e0ac8Smrg * VT382 1 plane (two fixed colors: black and white) FIXME: verify 671e0a2b6dfSmrg * dxterm ? 672e0a2b6dfSmrg */ 673f2e35a3aSmrg switch (screen->graphics_termid) { 674e0a2b6dfSmrg case 125: 675894e0ac8Smrg return 4U; 676e0a2b6dfSmrg case 240: 677894e0ac8Smrg return 4U; 678e0a2b6dfSmrg case 241: 679894e0ac8Smrg return 4U; 680e0a2b6dfSmrg case 330: 681894e0ac8Smrg return 4U; 682e0a2b6dfSmrg case 340: 683894e0ac8Smrg return 16U; 684894e0ac8Smrg case 382: 685894e0ac8Smrg return 2U; 686e0a2b6dfSmrg default: 687894e0ac8Smrg /* unknown graphics model -- might as well be generous */ 688894e0ac8Smrg return MAX_COLOR_REGISTERS; 689e0a2b6dfSmrg } 690894e0ac8Smrg} 691894e0ac8Smrg 692894e0ac8Smrgstatic void 693894e0ac8Smrginit_graphic(Graphic *graphic, 694894e0ac8Smrg unsigned type, 695f2e35a3aSmrg int graphics_termid, 696894e0ac8Smrg int charrow, 697894e0ac8Smrg int charcol, 698894e0ac8Smrg unsigned num_color_registers, 699894e0ac8Smrg int private_colors) 700894e0ac8Smrg{ 70101037d57Smrg const unsigned max_pixels = (unsigned) (graphic->max_width * 70201037d57Smrg graphic->max_height); 703894e0ac8Smrg unsigned i; 704894e0ac8Smrg 705f2e35a3aSmrg TRACE(("init_graphic at %d,%d\n", charrow, charcol)); 706894e0ac8Smrg 7072e4f8982Smrg graphic->hidden = 0; 708894e0ac8Smrg graphic->dirty = 1; 70901037d57Smrg for (i = 0U; i < max_pixels; i++) 710894e0ac8Smrg graphic->pixels[i] = COLOR_HOLE; 711894e0ac8Smrg memset(graphic->color_registers_used, 0, sizeof(graphic->color_registers_used)); 712e0a2b6dfSmrg 713e0a2b6dfSmrg /* 714e0a2b6dfSmrg * text and graphics interactions: 715e0a2b6dfSmrg * VK100/GIGI text writes on top of graphics buffer, color attribute shared with text 716e0a2b6dfSmrg * VT240,VT241,VT330,VT340 text writes on top of graphics buffer 717894e0ac8Smrg * VT382 text writes on top of graphics buffer FIXME: verify 718e0a2b6dfSmrg * VT125 graphics buffer overlaid on top of text in B&W display, text not present in color display 719e0a2b6dfSmrg */ 720e0a2b6dfSmrg 721894e0ac8Smrg /* 722894e0ac8Smrg * dimensions (ReGIS logical, physical): 723894e0ac8Smrg * VK100/GIGI 768x4?? 768x240(status?) 724894e0ac8Smrg * VT125 768x460 768x230(+10status) (1:2 aspect ratio, ReGIS halves vertical addresses through "odd y emulation") 725894e0ac8Smrg * VT240 800x460 800x230(+10status) (1:2 aspect ratio, ReGIS halves vertical addresses through "odd y emulation") 726894e0ac8Smrg * VT241 800x460 800x230(+10status) (1:2 aspect ratio, ReGIS halves vertical addresses through "odd y emulation") 727894e0ac8Smrg * VT330 800x480 800x480(+?status) 728894e0ac8Smrg * VT340 800x480 800x480(+?status) 729894e0ac8Smrg * VT382 960x750 sixel only 730894e0ac8Smrg * dxterm ?x? ?x? variable? 731894e0ac8Smrg */ 732e0a2b6dfSmrg 733e0a2b6dfSmrg graphic->actual_width = 0; 734e0a2b6dfSmrg graphic->actual_height = 0; 735e0a2b6dfSmrg 736894e0ac8Smrg graphic->pixw = 1; 737894e0ac8Smrg graphic->pixh = 1; 738894e0ac8Smrg 739894e0ac8Smrg graphic->valid_registers = num_color_registers; 740894e0ac8Smrg TRACE(("%d color registers\n", graphic->valid_registers)); 741894e0ac8Smrg 742e0a2b6dfSmrg graphic->private_colors = private_colors; 743e0a2b6dfSmrg if (graphic->private_colors) { 744894e0ac8Smrg TRACE(("using private color registers\n")); 745f2e35a3aSmrg init_color_registers(graphic->private_color_registers, graphics_termid); 746e0a2b6dfSmrg graphic->color_registers = graphic->private_color_registers; 747e0a2b6dfSmrg } else { 748894e0ac8Smrg TRACE(("using shared color registers\n")); 749894e0ac8Smrg graphic->color_registers = getSharedRegisters(); 750e0a2b6dfSmrg } 751e0a2b6dfSmrg 752894e0ac8Smrg graphic->charrow = charrow; 753894e0ac8Smrg graphic->charcol = charcol; 754894e0ac8Smrg graphic->type = type; 755e0a2b6dfSmrg graphic->valid = 0; 756e0a2b6dfSmrg} 757e0a2b6dfSmrg 758894e0ac8SmrgGraphic * 759894e0ac8Smrgget_new_graphic(XtermWidget xw, int charrow, int charcol, unsigned type) 760e0a2b6dfSmrg{ 761e0a2b6dfSmrg TScreen const *screen = TScreenOf(xw); 762e0a2b6dfSmrg int bufferid = screen->whichBuf; 763f2e35a3aSmrg int graphics_termid = GraphicsTermId(screen); 764f2e35a3aSmrg Graphic *graphic = NULL; 765894e0ac8Smrg unsigned ii; 766e0a2b6dfSmrg 767894e0ac8Smrg FOR_EACH_SLOT(ii) { 76801037d57Smrg if ((graphic = getInactiveSlot(screen, ii))) { 769894e0ac8Smrg TRACE(("using fresh graphic index=%u id=%u\n", ii, next_graphic_id)); 770e0a2b6dfSmrg break; 771894e0ac8Smrg } 772e0a2b6dfSmrg } 773e0a2b6dfSmrg 774894e0ac8Smrg /* if none are free, recycle the graphic scrolled back the farthest */ 775894e0ac8Smrg if (!graphic) { 776e0a2b6dfSmrg int min_charrow = 0; 777894e0ac8Smrg Graphic *min_graphic = NULL; 778e0a2b6dfSmrg 779894e0ac8Smrg FOR_EACH_SLOT(ii) { 780894e0ac8Smrg if (!(graphic = getActiveSlot(ii))) 781894e0ac8Smrg continue; 782e0a2b6dfSmrg if (!min_graphic || graphic->charrow < min_charrow) { 783e0a2b6dfSmrg min_charrow = graphic->charrow; 784e0a2b6dfSmrg min_graphic = graphic; 785e0a2b6dfSmrg } 786e0a2b6dfSmrg } 787894e0ac8Smrg TRACE(("recycling old graphic index=%u id=%u\n", ii, next_graphic_id)); 788e0a2b6dfSmrg graphic = min_graphic; 789e0a2b6dfSmrg } 790e0a2b6dfSmrg 791894e0ac8Smrg if (graphic) { 792894e0ac8Smrg unsigned num_color_registers; 793894e0ac8Smrg num_color_registers = get_color_register_count(screen); 794894e0ac8Smrg graphic->xw = xw; 795894e0ac8Smrg graphic->bufferid = bufferid; 796894e0ac8Smrg graphic->id = next_graphic_id++; 797894e0ac8Smrg init_graphic(graphic, 798894e0ac8Smrg type, 799f2e35a3aSmrg graphics_termid, 800894e0ac8Smrg charrow, 801894e0ac8Smrg charcol, 802894e0ac8Smrg num_color_registers, 803894e0ac8Smrg screen->privatecolorregisters); 804894e0ac8Smrg } 805e0a2b6dfSmrg return graphic; 806e0a2b6dfSmrg} 807e0a2b6dfSmrg 808894e0ac8SmrgGraphic * 809894e0ac8Smrgget_new_or_matching_graphic(XtermWidget xw, 810894e0ac8Smrg int charrow, 811894e0ac8Smrg int charcol, 812894e0ac8Smrg int actual_width, 813894e0ac8Smrg int actual_height, 814894e0ac8Smrg unsigned type) 815e0a2b6dfSmrg{ 816894e0ac8Smrg TScreen const *screen = TScreenOf(xw); 817894e0ac8Smrg int bufferid = screen->whichBuf; 818894e0ac8Smrg Graphic *graphic; 819894e0ac8Smrg unsigned ii; 820894e0ac8Smrg 821894e0ac8Smrg FOR_EACH_SLOT(ii) { 8222e4f8982Smrg TRACE(("checking slot=%u for graphic at %d,%d %dx%d bufferid=%d type=%u\n", ii, 8232e4f8982Smrg charrow, charcol, 8242e4f8982Smrg actual_width, actual_height, 8252e4f8982Smrg bufferid, type)); 8262e4f8982Smrg if ((graphic = getActiveSlot(ii))) { 8272e4f8982Smrg if (graphic->type == type && 8282e4f8982Smrg graphic->bufferid == bufferid && 8292e4f8982Smrg graphic->charrow == charrow && 8302e4f8982Smrg graphic->charcol == charcol && 8312e4f8982Smrg graphic->actual_width == actual_width && 8322e4f8982Smrg graphic->actual_height == actual_height) { 8332e4f8982Smrg TRACE(("found existing graphic slot=%u id=%u\n", ii, graphic->id)); 8342e4f8982Smrg return graphic; 8352e4f8982Smrg } 8362e4f8982Smrg TRACE(("not a match: graphic at %d,%d %dx%d bufferid=%d type=%u\n", 8372e4f8982Smrg graphic->charrow, graphic->charcol, 8382e4f8982Smrg graphic->actual_width, graphic->actual_height, 8392e4f8982Smrg graphic->bufferid, graphic->type)); 840e0a2b6dfSmrg } 841e0a2b6dfSmrg } 842e0a2b6dfSmrg 843894e0ac8Smrg /* if no match get a new graphic */ 844894e0ac8Smrg if ((graphic = get_new_graphic(xw, charrow, charcol, type))) { 845894e0ac8Smrg graphic->actual_width = actual_width; 846894e0ac8Smrg graphic->actual_height = actual_height; 8472e4f8982Smrg TRACE(("no match; created graphic at %d,%d %dx%d bufferid=%d type=%u\n", 8482e4f8982Smrg graphic->charrow, graphic->charcol, 8492e4f8982Smrg graphic->actual_width, graphic->actual_height, 8502e4f8982Smrg graphic->bufferid, graphic->type)); 851e0a2b6dfSmrg } 852894e0ac8Smrg return graphic; 853e0a2b6dfSmrg} 854e0a2b6dfSmrg 85501037d57Smrg#define ScaleForXColor(s) (unsigned short) ((long)(s) * 65535 / CHANNEL_MAX) 85601037d57Smrg 85701037d57Smrgstatic int 85801037d57Smrgsave_allocated_color(const ColorRegister *reg, XtermWidget xw, Pixel *pix) 85901037d57Smrg{ 86001037d57Smrg unsigned const rr = ((unsigned) reg->r * (LOOKUP_WIDTH - 1)) / CHANNEL_MAX; 86101037d57Smrg unsigned const gg = ((unsigned) reg->g * (LOOKUP_WIDTH - 1)) / CHANNEL_MAX; 86201037d57Smrg unsigned const bb = ((unsigned) reg->b * (LOOKUP_WIDTH - 1)) / CHANNEL_MAX; 86301037d57Smrg XColor xcolor; 86401037d57Smrg AllocatedColorRegister *new_color; 86501037d57Smrg 86601037d57Smrg xcolor.pixel = 0UL; 86701037d57Smrg xcolor.red = ScaleForXColor(reg->r); 86801037d57Smrg xcolor.green = ScaleForXColor(reg->g); 86901037d57Smrg xcolor.blue = ScaleForXColor(reg->b); 87001037d57Smrg xcolor.flags = DoRed | DoGreen | DoBlue; 87101037d57Smrg if (!allocateBestRGB(xw, &xcolor)) { 87201037d57Smrg TRACE(("unable to allocate xcolor\n")); 87301037d57Smrg *pix = 0UL; 87401037d57Smrg return 0; 875f2e35a3aSmrg } else { 876f2e35a3aSmrg *pix = xcolor.pixel; 87701037d57Smrg 878f2e35a3aSmrg if (!(new_color = malloc(sizeof(*new_color)))) { 879f2e35a3aSmrg TRACE(("unable to save pixel %lu\n", (unsigned long) *pix)); 880f2e35a3aSmrg return 0; 881f2e35a3aSmrg } else { 882f2e35a3aSmrg new_color->r = reg->r; 883f2e35a3aSmrg new_color->g = reg->g; 884f2e35a3aSmrg new_color->b = reg->b; 885f2e35a3aSmrg new_color->pix = *pix; 886f2e35a3aSmrg new_color->next = allocated_colors[rr][gg][bb]; 88701037d57Smrg 888f2e35a3aSmrg allocated_colors[rr][gg][bb] = new_color; 88901037d57Smrg 890f2e35a3aSmrg return 1; 891f2e35a3aSmrg } 892f2e35a3aSmrg } 89301037d57Smrg} 89401037d57Smrg 895f2e35a3aSmrg/* FIXME: with so many possible colors we need to determine 896f2e35a3aSmrg * when to free them to be nice to PseudoColor displays 897f2e35a3aSmrg */ 89801037d57Smrgstatic Pixel 89901037d57Smrgcolor_register_to_xpixel(const ColorRegister *reg, XtermWidget xw) 90001037d57Smrg{ 901f2e35a3aSmrg Pixel result; 902f2e35a3aSmrg unsigned const rr = ((unsigned) reg->r * (LOOKUP_WIDTH - 1)) / CHANNEL_MAX; 903f2e35a3aSmrg unsigned const gg = ((unsigned) reg->g * (LOOKUP_WIDTH - 1)) / CHANNEL_MAX; 904f2e35a3aSmrg unsigned const bb = ((unsigned) reg->b * (LOOKUP_WIDTH - 1)) / CHANNEL_MAX; 905f2e35a3aSmrg const AllocatedColorRegister *search; 90601037d57Smrg 907f2e35a3aSmrg for (search = allocated_colors[rr][gg][bb]; search; search = search->next) { 908f2e35a3aSmrg if (search->r == reg->r && 909f2e35a3aSmrg search->g == reg->g && 910f2e35a3aSmrg search->b == reg->b) { 911f2e35a3aSmrg return search->pix; 912f2e35a3aSmrg } 913f2e35a3aSmrg } 91401037d57Smrg 915f2e35a3aSmrg save_allocated_color(reg, xw, &result); 916f2e35a3aSmrg return result; 917e0a2b6dfSmrg} 918e0a2b6dfSmrg 919e0a2b6dfSmrgstatic void 920894e0ac8Smrgrefresh_graphic(TScreen const *screen, 921894e0ac8Smrg Graphic const *graphic, 92201037d57Smrg ColorRegister *buffer, 92301037d57Smrg int refresh_x, 92401037d57Smrg int refresh_y, 92501037d57Smrg int refresh_w, 92601037d57Smrg int refresh_h, 92701037d57Smrg int draw_x, 92801037d57Smrg int draw_y, 92901037d57Smrg int draw_w, 93001037d57Smrg int draw_h) 931e0a2b6dfSmrg{ 93201037d57Smrg int const pw = graphic->pixw; 93301037d57Smrg int const ph = graphic->pixh; 93401037d57Smrg int const graph_x = graphic->charcol * FontWidth(screen); 93501037d57Smrg int const graph_y = graphic->charrow * FontHeight(screen); 93601037d57Smrg int const graph_w = graphic->actual_width; 93701037d57Smrg int const graph_h = graphic->actual_height; 93801037d57Smrg int const mw = graphic->max_width; 939e0a2b6dfSmrg int r, c; 94001037d57Smrg int holes, total, out_of_range; 94101037d57Smrg RegisterNum regnum; 942e0a2b6dfSmrg 94301037d57Smrg TRACE(("refreshing graphic %u from %d,%d %dx%d (valid=%d, size=%dx%d, scale=%dx%d max=%dx%d)\n", 94401037d57Smrg graphic->id, 94501037d57Smrg graph_x, graph_y, draw_w, draw_h, 946e0a2b6dfSmrg graphic->valid, 947e0a2b6dfSmrg graphic->actual_width, 948e0a2b6dfSmrg graphic->actual_height, 94901037d57Smrg pw, ph, 950e0a2b6dfSmrg graphic->max_width, 95101037d57Smrg graphic->max_height)); 952e0a2b6dfSmrg 95301037d57Smrg TRACE(("refresh pixmap starts at %d,%d\n", refresh_x, refresh_y)); 954e0a2b6dfSmrg 955e0a2b6dfSmrg holes = total = 0; 95601037d57Smrg out_of_range = 0; 95701037d57Smrg for (r = 0; r < graph_h; r++) { 95801037d57Smrg int pmy = graph_y + r * ph; 959894e0ac8Smrg 96001037d57Smrg if (pmy + ph - 1 < draw_y) 961894e0ac8Smrg continue; 96201037d57Smrg if (pmy > draw_y + draw_h - 1) 96301037d57Smrg break; 964894e0ac8Smrg 965f2e35a3aSmrg if (pmy < draw_y || pmy > draw_y + draw_h - 1 || 966f2e35a3aSmrg pmy < refresh_y || pmy > refresh_y + refresh_h - 1) { 967f2e35a3aSmrg out_of_range++; 968f2e35a3aSmrg continue; 969f2e35a3aSmrg } 970f2e35a3aSmrg 97101037d57Smrg for (c = 0; c < graph_w; c++) { 97201037d57Smrg int pmx = graph_x + c * pw; 973e0a2b6dfSmrg 97401037d57Smrg if (pmx + pw - 1 < draw_x) 975894e0ac8Smrg continue; 97601037d57Smrg if (pmx > draw_x + draw_w - 1) 97701037d57Smrg break; 978e0a2b6dfSmrg 979f2e35a3aSmrg if (pmx < draw_x || pmx > draw_x + draw_w - 1 || 980f2e35a3aSmrg pmx < refresh_x || pmx > refresh_x + refresh_w - 1) { 981f2e35a3aSmrg out_of_range++; 982f2e35a3aSmrg continue; 983f2e35a3aSmrg } 984f2e35a3aSmrg 985e0a2b6dfSmrg total++; 98601037d57Smrg regnum = graphic->pixels[r * mw + c]; 98701037d57Smrg if (regnum == COLOR_HOLE) { 988e0a2b6dfSmrg holes++; 989f2e35a3aSmrg } else { 990f2e35a3aSmrg buffer[(pmy - refresh_y) * refresh_w + 991f2e35a3aSmrg (pmx - refresh_x)] = 992f2e35a3aSmrg graphic->color_registers[regnum]; 993e0a2b6dfSmrg } 994e0a2b6dfSmrg } 995894e0ac8Smrg } 996e0a2b6dfSmrg 99701037d57Smrg TRACE(("done refreshing graphic: %d of %d refreshed pixels were holes; %d were out of pixmap range\n", 99801037d57Smrg holes, total, out_of_range)); 99901037d57Smrg} 100001037d57Smrg 1001e0a2b6dfSmrg#ifdef DEBUG_REFRESH 100201037d57Smrg 100301037d57Smrg#define BASEX(X) ( (draw_x - base_x) + (X) ) 100401037d57Smrg#define BASEY(Y) ( (draw_y - base_y) + (Y) ) 100501037d57Smrg 100601037d57Smrgstatic void 100701037d57Smrgoutline_refresh(TScreen const *screen, 100801037d57Smrg Graphic const *graphic, 100901037d57Smrg Pixmap output_pm, 101001037d57Smrg GC graphics_gc, 101101037d57Smrg int base_x, 101201037d57Smrg int base_y, 101301037d57Smrg int draw_x, 101401037d57Smrg int draw_y, 101501037d57Smrg int draw_w, 101601037d57Smrg int draw_h) 101701037d57Smrg{ 101801037d57Smrg Display *const display = screen->display; 101901037d57Smrg int const pw = graphic->pixw; 102001037d57Smrg int const ph = graphic->pixh; 102101037d57Smrg XGCValues xgcv; 102201037d57Smrg XColor def; 102301037d57Smrg 102401037d57Smrg def.red = (unsigned short) ((1.0 - 0.1 * (rand() / (double) 102501037d57Smrg RAND_MAX) * 65535.0)); 102601037d57Smrg def.green = (unsigned short) ((0.7 + 0.2 * (rand() / (double) 102701037d57Smrg RAND_MAX)) * 65535.0); 102801037d57Smrg def.blue = (unsigned short) ((0.1 + 0.1 * (rand() / (double) 102901037d57Smrg RAND_MAX)) * 65535.0); 103001037d57Smrg def.flags = DoRed | DoGreen | DoBlue; 103101037d57Smrg if (allocateBestRGB(graphic->xw, &def)) { 103201037d57Smrg xgcv.foreground = def.pixel; 103301037d57Smrg XChangeGC(display, graphics_gc, GCForeground, &xgcv); 1034e0a2b6dfSmrg } 1035e0a2b6dfSmrg 103601037d57Smrg XDrawLine(display, output_pm, graphics_gc, 103701037d57Smrg BASEX(0), BASEY(0), 103801037d57Smrg BASEX(draw_w - 1), BASEY(0)); 103901037d57Smrg XDrawLine(display, output_pm, graphics_gc, 104001037d57Smrg BASEX(0), BASEY(draw_h - 1), 104101037d57Smrg BASEX(draw_w - 1), BASEY(draw_h - 1)); 104201037d57Smrg 104301037d57Smrg XDrawLine(display, output_pm, graphics_gc, 104401037d57Smrg BASEX(0), BASEY(0), 104501037d57Smrg BASEX(0), BASEY(draw_h - 1)); 104601037d57Smrg XDrawLine(display, output_pm, graphics_gc, 104701037d57Smrg BASEX(draw_w - 1), BASEY(0), 104801037d57Smrg BASEX(draw_w - 1), BASEY(draw_h - 1)); 104901037d57Smrg 105001037d57Smrg XDrawLine(display, output_pm, graphics_gc, 105101037d57Smrg BASEX(draw_w - 1), BASEY(0), 105201037d57Smrg BASEX(0), BASEY(draw_h - 1)); 105301037d57Smrg XDrawLine(display, output_pm, graphics_gc, 105401037d57Smrg BASEX(draw_w - 1), BASEY(draw_h - 1), 105501037d57Smrg BASEX(0), BASEY(0)); 105601037d57Smrg 105701037d57Smrg def.red = (short) (0.7 * 65535.0); 105801037d57Smrg def.green = (short) (0.1 * 65535.0); 105901037d57Smrg def.blue = (short) (1.0 * 65535.0); 106001037d57Smrg def.flags = DoRed | DoGreen | DoBlue; 106101037d57Smrg if (allocateBestRGB(graphic->xw, &def)) { 106201037d57Smrg xgcv.foreground = def.pixel; 106301037d57Smrg XChangeGC(display, graphics_gc, GCForeground, &xgcv); 106401037d57Smrg } 106501037d57Smrg XFillRectangle(display, output_pm, graphics_gc, 106601037d57Smrg BASEX(0), 106701037d57Smrg BASEY(0), 106801037d57Smrg (unsigned) pw, (unsigned) ph); 106901037d57Smrg XFillRectangle(display, output_pm, graphics_gc, 107001037d57Smrg BASEX(draw_w - 1 - pw), 107101037d57Smrg BASEY(draw_h - 1 - ph), 107201037d57Smrg (unsigned) pw, (unsigned) ph); 1073e0a2b6dfSmrg} 107401037d57Smrg#endif 1075e0a2b6dfSmrg 1076e0a2b6dfSmrg/* 1077e0a2b6dfSmrg * Primary color hues: 1078e0a2b6dfSmrg * blue: 0 degrees 1079e0a2b6dfSmrg * red: 120 degrees 1080e0a2b6dfSmrg * green: 240 degrees 1081e0a2b6dfSmrg */ 1082894e0ac8Smrgvoid 1083e0a2b6dfSmrghls2rgb(int h, int l, int s, short *r, short *g, short *b) 1084e0a2b6dfSmrg{ 108501037d57Smrg const int hs = ((h + 240) / 60) % 6; 108601037d57Smrg const double lv = l / 100.0; 108701037d57Smrg const double sv = s / 100.0; 1088894e0ac8Smrg double c, x, m, c2; 1089e0a2b6dfSmrg double r1, g1, b1; 1090e0a2b6dfSmrg 1091e0a2b6dfSmrg if (s == 0) { 1092e0a2b6dfSmrg *r = *g = *b = (short) l; 1093e0a2b6dfSmrg return; 1094e0a2b6dfSmrg } 1095e0a2b6dfSmrg 109601037d57Smrg c2 = (2.0 * lv) - 1.0; 109701037d57Smrg if (c2 < 0.0) 1098894e0ac8Smrg c2 = -c2; 1099894e0ac8Smrg c = (1.0 - c2) * sv; 110001037d57Smrg x = (hs & 1) ? c : 0.0; 1101e0a2b6dfSmrg m = lv - 0.5 * c; 1102e0a2b6dfSmrg 110301037d57Smrg switch (hs) { 1104e0a2b6dfSmrg case 0: 1105e0a2b6dfSmrg r1 = c; 1106e0a2b6dfSmrg g1 = x; 1107e0a2b6dfSmrg b1 = 0.0; 1108e0a2b6dfSmrg break; 1109e0a2b6dfSmrg case 1: 1110e0a2b6dfSmrg r1 = x; 1111e0a2b6dfSmrg g1 = c; 1112e0a2b6dfSmrg b1 = 0.0; 1113e0a2b6dfSmrg break; 1114e0a2b6dfSmrg case 2: 1115e0a2b6dfSmrg r1 = 0.0; 1116e0a2b6dfSmrg g1 = c; 1117e0a2b6dfSmrg b1 = x; 1118e0a2b6dfSmrg break; 1119e0a2b6dfSmrg case 3: 1120e0a2b6dfSmrg r1 = 0.0; 1121e0a2b6dfSmrg g1 = x; 1122e0a2b6dfSmrg b1 = c; 1123e0a2b6dfSmrg break; 1124e0a2b6dfSmrg case 4: 1125e0a2b6dfSmrg r1 = x; 1126e0a2b6dfSmrg g1 = 0.0; 1127e0a2b6dfSmrg b1 = c; 1128e0a2b6dfSmrg break; 1129e0a2b6dfSmrg case 5: 1130e0a2b6dfSmrg r1 = c; 1131e0a2b6dfSmrg g1 = 0.0; 1132e0a2b6dfSmrg b1 = x; 1133e0a2b6dfSmrg break; 1134e0a2b6dfSmrg default: 1135894e0ac8Smrg TRACE(("Bad HLS input: [%d,%d,%d], returning white\n", h, l, s)); 1136e0a2b6dfSmrg *r = (short) 100; 1137e0a2b6dfSmrg *g = (short) 100; 1138e0a2b6dfSmrg *b = (short) 100; 1139e0a2b6dfSmrg return; 1140e0a2b6dfSmrg } 1141e0a2b6dfSmrg 1142e0a2b6dfSmrg *r = (short) ((r1 + m) * 100.0 + 0.5); 1143e0a2b6dfSmrg *g = (short) ((g1 + m) * 100.0 + 0.5); 1144e0a2b6dfSmrg *b = (short) ((b1 + m) * 100.0 + 0.5); 1145e0a2b6dfSmrg 1146e0a2b6dfSmrg if (*r < 0) 1147e0a2b6dfSmrg *r = 0; 1148e0a2b6dfSmrg else if (*r > 100) 1149e0a2b6dfSmrg *r = 100; 1150e0a2b6dfSmrg if (*g < 0) 1151e0a2b6dfSmrg *g = 0; 1152e0a2b6dfSmrg else if (*g > 100) 1153e0a2b6dfSmrg *g = 100; 1154e0a2b6dfSmrg if (*b < 0) 1155e0a2b6dfSmrg *b = 0; 1156e0a2b6dfSmrg else if (*b > 100) 1157e0a2b6dfSmrg *b = 100; 1158e0a2b6dfSmrg} 1159e0a2b6dfSmrg 1160894e0ac8Smrgvoid 1161894e0ac8Smrgdump_graphic(Graphic const *graphic) 1162e0a2b6dfSmrg{ 1163894e0ac8Smrg#if defined(DUMP_COLORS) || defined(DUMP_BITMAP) 1164894e0ac8Smrg RegisterNum color; 1165894e0ac8Smrg#endif 1166894e0ac8Smrg#ifdef DUMP_BITMAP 1167894e0ac8Smrg int r, c; 1168894e0ac8Smrg ColorRegister const *reg; 1169894e0ac8Smrg#endif 1170e0a2b6dfSmrg 1171894e0ac8Smrg (void) graphic; 1172e0a2b6dfSmrg 1173894e0ac8Smrg TRACE(("graphic stats: id=%u charrow=%d charcol=%d actual_width=%d actual_height=%d pixw=%d pixh=%d\n", 1174894e0ac8Smrg graphic->id, 1175894e0ac8Smrg graphic->charrow, 1176894e0ac8Smrg graphic->charcol, 1177894e0ac8Smrg graphic->actual_width, 1178894e0ac8Smrg graphic->actual_height, 1179894e0ac8Smrg graphic->pixw, 1180894e0ac8Smrg graphic->pixh)); 1181e0a2b6dfSmrg 1182894e0ac8Smrg#ifdef DUMP_COLORS 1183894e0ac8Smrg TRACE(("graphic colors:\n")); 1184894e0ac8Smrg for (color = 0; color < graphic->valid_registers; color++) { 1185894e0ac8Smrg TRACE(("%03u: %d,%d,%d\n", 1186894e0ac8Smrg color, 1187894e0ac8Smrg graphic->color_registers[color].r, 1188894e0ac8Smrg graphic->color_registers[color].g, 1189894e0ac8Smrg graphic->color_registers[color].b)); 1190e0a2b6dfSmrg } 1191e0a2b6dfSmrg#endif 1192e0a2b6dfSmrg 1193894e0ac8Smrg#ifdef DUMP_BITMAP 1194894e0ac8Smrg TRACE(("graphic pixels:\n")); 1195894e0ac8Smrg for (r = 0; r < graphic->actual_height; r++) { 1196894e0ac8Smrg for (c = 0; c < graphic->actual_width; c++) { 1197894e0ac8Smrg color = graphic->pixels[r * graphic->max_width + c]; 1198894e0ac8Smrg if (color == COLOR_HOLE) { 1199894e0ac8Smrg TRACE(("?")); 1200e0a2b6dfSmrg } else { 1201894e0ac8Smrg reg = &graphic->color_registers[color]; 1202894e0ac8Smrg if (reg->r + reg->g + reg->b > 200) { 1203894e0ac8Smrg TRACE(("#")); 1204894e0ac8Smrg } else if (reg->r + reg->g + reg->b > 150) { 1205894e0ac8Smrg TRACE(("%%")); 1206894e0ac8Smrg } else if (reg->r + reg->g + reg->b > 100) { 1207894e0ac8Smrg TRACE((":")); 1208894e0ac8Smrg } else if (reg->r + reg->g + reg->b > 80) { 1209894e0ac8Smrg TRACE((".")); 1210894e0ac8Smrg } else { 1211894e0ac8Smrg TRACE((" ")); 1212e0a2b6dfSmrg } 1213e0a2b6dfSmrg } 1214e0a2b6dfSmrg } 1215894e0ac8Smrg TRACE(("\n")); 1216e0a2b6dfSmrg } 1217e0a2b6dfSmrg 1218894e0ac8Smrg TRACE(("\n")); 1219894e0ac8Smrg#endif 1220e0a2b6dfSmrg} 1221e0a2b6dfSmrg 1222e0a2b6dfSmrg/* Erase the portion of any displayed graphic overlapping with a rectangle 122301037d57Smrg * of the given size and location in pixels relative to the start of the 122401037d57Smrg * graphic. This is used to allow text to "erase" graphics underneath it. 1225e0a2b6dfSmrg */ 1226e0a2b6dfSmrgstatic void 1227894e0ac8Smrgerase_graphic(Graphic *graphic, int x, int y, int w, int h) 1228e0a2b6dfSmrg{ 1229e0a2b6dfSmrg RegisterNum hole = COLOR_HOLE; 1230e0a2b6dfSmrg int pw, ph; 1231e0a2b6dfSmrg int r, c; 1232894e0ac8Smrg int rbase, cbase; 1233e0a2b6dfSmrg 1234e0a2b6dfSmrg pw = graphic->pixw; 1235e0a2b6dfSmrg ph = graphic->pixh; 1236e0a2b6dfSmrg 1237894e0ac8Smrg TRACE(("erasing graphic %d,%d %dx%d\n", x, y, w, h)); 1238e0a2b6dfSmrg 1239894e0ac8Smrg rbase = 0; 1240e0a2b6dfSmrg for (r = 0; r < graphic->actual_height; r++) { 1241894e0ac8Smrg if (rbase + ph - 1 >= y 1242894e0ac8Smrg && rbase <= y + h - 1) { 1243894e0ac8Smrg cbase = 0; 1244894e0ac8Smrg for (c = 0; c < graphic->actual_width; c++) { 1245894e0ac8Smrg if (cbase + pw - 1 >= x 1246894e0ac8Smrg && cbase <= x + w - 1) { 1247894e0ac8Smrg graphic->pixels[r * graphic->max_width + c] = hole; 1248894e0ac8Smrg } 1249894e0ac8Smrg cbase += pw; 1250894e0ac8Smrg } 1251e0a2b6dfSmrg } 1252894e0ac8Smrg rbase += ph; 1253e0a2b6dfSmrg } 1254e0a2b6dfSmrg} 1255e0a2b6dfSmrg 1256e0a2b6dfSmrgstatic int 1257894e0ac8Smrgcompare_graphic_ids(const void *left, const void *right) 1258e0a2b6dfSmrg{ 1259894e0ac8Smrg const Graphic *l = *(const Graphic *const *) left; 1260894e0ac8Smrg const Graphic *r = *(const Graphic *const *) right; 1261e0a2b6dfSmrg 1262e0a2b6dfSmrg if (!l->valid || !r->valid) 1263e0a2b6dfSmrg return 0; 126401037d57Smrg 126501037d57Smrg if (l->bufferid < r->bufferid) 126601037d57Smrg return -1; 126701037d57Smrg else if (l->bufferid > r->bufferid) 126801037d57Smrg return 1; 126901037d57Smrg 1270e0a2b6dfSmrg if (l->id < r->id) 1271e0a2b6dfSmrg return -1; 1272e0a2b6dfSmrg else 1273e0a2b6dfSmrg return 1; 1274e0a2b6dfSmrg} 1275e0a2b6dfSmrg 127601037d57Smrgstatic void 127701037d57Smrgclip_area(int *orig_x, int *orig_y, int *orig_w, int *orig_h, 127801037d57Smrg int clip_x, int clip_y, int clip_w, int clip_h) 1279e0a2b6dfSmrg{ 128001037d57Smrg if (*orig_x < clip_x) { 128101037d57Smrg const int diff = clip_x - *orig_x; 128201037d57Smrg *orig_x += diff; 128301037d57Smrg *orig_w -= diff; 128401037d57Smrg } 128501037d57Smrg if (*orig_w > 0 && *orig_x + *orig_w > clip_x + clip_w) { 128601037d57Smrg *orig_w -= (*orig_x + *orig_w) - (clip_x + clip_w); 128701037d57Smrg } 128801037d57Smrg 128901037d57Smrg if (*orig_y < clip_y) { 129001037d57Smrg const int diff = clip_y - *orig_y; 129101037d57Smrg *orig_y += diff; 129201037d57Smrg *orig_h -= diff; 129301037d57Smrg } 129401037d57Smrg if (*orig_h > 0 && *orig_y + *orig_h > clip_y + clip_h) { 129501037d57Smrg *orig_h -= (*orig_y + *orig_h) - (clip_y + clip_h); 129601037d57Smrg } 129701037d57Smrg} 129801037d57Smrg 129901037d57Smrg/* the coordinates are relative to the screen */ 130001037d57Smrgstatic void 130101037d57Smrgrefresh_graphics(XtermWidget xw, 130201037d57Smrg int leftcol, 130301037d57Smrg int toprow, 130401037d57Smrg int ncols, 130501037d57Smrg int nrows, 130601037d57Smrg int skip_clean) 130701037d57Smrg{ 130801037d57Smrg TScreen *const screen = TScreenOf(xw); 130901037d57Smrg Display *const display = screen->display; 131001037d57Smrg Window const drawable = VDrawable(screen); 131101037d57Smrg int const scroll_y = screen->topline * FontHeight(screen); 131201037d57Smrg int const refresh_x = leftcol * FontWidth(screen); 131301037d57Smrg int const refresh_y = toprow * FontHeight(screen) + scroll_y; 131401037d57Smrg int const refresh_w = ncols * FontWidth(screen); 131501037d57Smrg int const refresh_h = nrows * FontHeight(screen); 131601037d57Smrg int draw_x_min, draw_x_max; 131701037d57Smrg int draw_y_min, draw_y_max; 1318894e0ac8Smrg Graphic *ordered_graphics[MAX_GRAPHICS]; 131901037d57Smrg unsigned ii, jj; 132001037d57Smrg unsigned active_count; 132101037d57Smrg unsigned holes, non_holes; 132201037d57Smrg int xx, yy; 132301037d57Smrg ColorRegister *buffer; 1324e0a2b6dfSmrg 132501037d57Smrg active_count = 0; 1326894e0ac8Smrg FOR_EACH_SLOT(ii) { 132701037d57Smrg Graphic *graphic; 132801037d57Smrg if (!(graphic = getActiveSlot(ii))) 132901037d57Smrg continue; 133001037d57Smrg TRACE(("refreshing graphic %d on buffer %d, current buffer %d\n", 133101037d57Smrg graphic->id, graphic->bufferid, screen->whichBuf)); 133201037d57Smrg if (screen->whichBuf == 0) { 133301037d57Smrg if (graphic->bufferid != 0) { 133401037d57Smrg TRACE(("skipping graphic %d from alt buffer (%d) when drawing screen=%d\n", 133501037d57Smrg graphic->id, graphic->bufferid, screen->whichBuf)); 133601037d57Smrg continue; 133701037d57Smrg } 133801037d57Smrg } else { 133901037d57Smrg if (graphic->bufferid == 0 && graphic->charrow >= 0) { 134001037d57Smrg TRACE(("skipping graphic %d from normal buffer (%d) when drawing screen=%d because it is not in scrollback area\n", 134101037d57Smrg graphic->id, graphic->bufferid, screen->whichBuf)); 134201037d57Smrg continue; 134301037d57Smrg } 134401037d57Smrg if (graphic->bufferid == 1 && 134501037d57Smrg graphic->charrow + (graphic->actual_height + 134601037d57Smrg FontHeight(screen) - 1) / 134701037d57Smrg FontHeight(screen) < 0) { 134801037d57Smrg TRACE(("skipping graphic %d from alt buffer (%d) when drawing screen=%d because it is completely in scrollback area\n", 134901037d57Smrg graphic->id, graphic->bufferid, screen->whichBuf)); 135001037d57Smrg continue; 135101037d57Smrg } 1352894e0ac8Smrg } 13532e4f8982Smrg if (graphic->hidden) 13542e4f8982Smrg continue; 135501037d57Smrg ordered_graphics[active_count++] = graphic; 1356894e0ac8Smrg } 135701037d57Smrg 135801037d57Smrg if (active_count == 0) 135901037d57Smrg return; 136001037d57Smrg if (active_count > 1) { 1361894e0ac8Smrg qsort(ordered_graphics, 136201037d57Smrg (size_t) active_count, 1363894e0ac8Smrg sizeof(ordered_graphics[0]), 1364894e0ac8Smrg compare_graphic_ids); 1365e0a2b6dfSmrg } 1366e0a2b6dfSmrg 136701037d57Smrg if (skip_clean) { 136801037d57Smrg unsigned skip_count; 1369e0a2b6dfSmrg 137001037d57Smrg for (jj = 0; jj < active_count; ++jj) { 137101037d57Smrg if (ordered_graphics[jj]->dirty) 137201037d57Smrg break; 1373e0a2b6dfSmrg } 137401037d57Smrg skip_count = jj; 137501037d57Smrg if (skip_count == active_count) 137601037d57Smrg return; 1377e0a2b6dfSmrg 137801037d57Smrg active_count -= skip_count; 137901037d57Smrg for (jj = 0; jj < active_count; ++jj) { 138001037d57Smrg ordered_graphics[jj] = ordered_graphics[jj + skip_count]; 138101037d57Smrg } 1382e0a2b6dfSmrg } 1383e0a2b6dfSmrg 138401037d57Smrg if (!(buffer = malloc(sizeof(*buffer) * 138501037d57Smrg (unsigned) refresh_w * (unsigned) refresh_h))) { 138601037d57Smrg TRACE(("unable to allocate %dx%d buffer for graphics refresh\n", 138701037d57Smrg refresh_w, refresh_h)); 138801037d57Smrg return; 138901037d57Smrg } 139001037d57Smrg for (yy = 0; yy < refresh_h; yy++) { 139101037d57Smrg for (xx = 0; xx < refresh_w; xx++) { 1392f2e35a3aSmrg buffer[yy * refresh_w + xx] = null_color; 139301037d57Smrg } 139401037d57Smrg } 1395e0a2b6dfSmrg 139601037d57Smrg TRACE(("refresh: screen->topline=%d leftcol=%d toprow=%d nrows=%d ncols=%d (%d,%d %dx%d)\n", 139701037d57Smrg screen->topline, 139801037d57Smrg leftcol, toprow, 139901037d57Smrg nrows, ncols, 140001037d57Smrg refresh_x, refresh_y, 140101037d57Smrg refresh_w, refresh_h)); 1402e0a2b6dfSmrg 140301037d57Smrg { 140401037d57Smrg int const altarea_x = 0; 140501037d57Smrg int const altarea_y = 0; 140601037d57Smrg int const altarea_w = Width(screen) * FontWidth(screen); 140701037d57Smrg int const altarea_h = Height(screen) * FontHeight(screen); 140801037d57Smrg 140901037d57Smrg int const scrollarea_x = 0; 141001037d57Smrg int const scrollarea_y = scroll_y; 141101037d57Smrg int const scrollarea_w = Width(screen) * FontWidth(screen); 141201037d57Smrg int const scrollarea_h = -scroll_y; 141301037d57Smrg 141401037d57Smrg int const mainarea_x = 0; 141501037d57Smrg int const mainarea_y = scroll_y; 141601037d57Smrg int const mainarea_w = Width(screen) * FontWidth(screen); 141701037d57Smrg int const mainarea_h = -scroll_y + Height(screen) * FontHeight(screen); 141801037d57Smrg 141901037d57Smrg draw_x_min = refresh_x + refresh_w; 142001037d57Smrg draw_x_max = refresh_x - 1; 142101037d57Smrg draw_y_min = refresh_y + refresh_h; 142201037d57Smrg draw_y_max = refresh_y - 1; 142301037d57Smrg for (jj = 0; jj < active_count; ++jj) { 142401037d57Smrg Graphic *graphic = ordered_graphics[jj]; 142501037d57Smrg int draw_x = graphic->charcol * FontWidth(screen); 142601037d57Smrg int draw_y = graphic->charrow * FontHeight(screen); 142701037d57Smrg int draw_w = graphic->actual_width; 142801037d57Smrg int draw_h = graphic->actual_height; 142901037d57Smrg 143001037d57Smrg if (screen->whichBuf != 0) { 143101037d57Smrg if (graphic->bufferid != 0) { 143201037d57Smrg /* clip to alt buffer */ 143301037d57Smrg clip_area(&draw_x, &draw_y, &draw_w, &draw_h, 143401037d57Smrg altarea_x, altarea_y, altarea_w, altarea_h); 14352e4f8982Smrg } else { 143601037d57Smrg /* clip to scrollback area */ 143701037d57Smrg clip_area(&draw_x, &draw_y, &draw_w, &draw_h, 143801037d57Smrg scrollarea_x, scrollarea_y, 143901037d57Smrg scrollarea_w, scrollarea_h); 144001037d57Smrg } 144101037d57Smrg } else { 144201037d57Smrg /* clip to scrollback + normal area */ 144301037d57Smrg clip_area(&draw_x, &draw_y, &draw_w, &draw_h, 144401037d57Smrg mainarea_x, mainarea_y, 144501037d57Smrg mainarea_w, mainarea_h); 144601037d57Smrg } 144701037d57Smrg 144801037d57Smrg clip_area(&draw_x, &draw_y, &draw_w, &draw_h, 144901037d57Smrg refresh_x, refresh_y, refresh_w, refresh_h); 145001037d57Smrg 145101037d57Smrg TRACE(("refresh: graph=%u\n", jj)); 145201037d57Smrg TRACE((" refresh_x=%d refresh_y=%d refresh_w=%d refresh_h=%d\n", 145301037d57Smrg refresh_x, refresh_y, refresh_w, refresh_h)); 145401037d57Smrg TRACE((" draw_x=%d draw_y=%d draw_w=%d draw_h=%d\n", 145501037d57Smrg draw_x, draw_y, draw_w, draw_h)); 145601037d57Smrg 145701037d57Smrg if (draw_w > 0 && draw_h > 0) { 145801037d57Smrg refresh_graphic(screen, graphic, buffer, 145901037d57Smrg refresh_x, refresh_y, 146001037d57Smrg refresh_w, refresh_h, 146101037d57Smrg draw_x, draw_y, 146201037d57Smrg draw_w, draw_h); 146301037d57Smrg if (draw_x < draw_x_min) 146401037d57Smrg draw_x_min = draw_x; 146501037d57Smrg if (draw_x + draw_w - 1 > draw_x_max) 146601037d57Smrg draw_x_max = draw_x + draw_w - 1; 146701037d57Smrg if (draw_y < draw_y_min) 146801037d57Smrg draw_y_min = draw_y; 146901037d57Smrg if (draw_y + draw_h - 1 > draw_y_max) 147001037d57Smrg draw_y_max = draw_y + draw_h - 1; 147101037d57Smrg } 147201037d57Smrg graphic->dirty = 0; 1473e0a2b6dfSmrg } 147401037d57Smrg } 1475e0a2b6dfSmrg 147601037d57Smrg if (draw_x_max < refresh_x || 147701037d57Smrg draw_x_min > refresh_x + refresh_w - 1 || 147801037d57Smrg draw_y_max < refresh_y || 147901037d57Smrg draw_y_min > refresh_y + refresh_h - 1) { 148001037d57Smrg free(buffer); 148101037d57Smrg return; 1482e0a2b6dfSmrg } 148301037d57Smrg 148401037d57Smrg holes = 0U; 148501037d57Smrg non_holes = 0U; 1486f2e35a3aSmrg { 1487f2e35a3aSmrg int y_min = draw_y_min - refresh_y; 1488f2e35a3aSmrg int y_max = draw_y_max - refresh_y; 1489f2e35a3aSmrg int x_min = draw_x_min - refresh_x; 1490f2e35a3aSmrg int x_max = draw_x_max - refresh_x; 1491f2e35a3aSmrg const ColorRegister *base = buffer + (y_min * refresh_w); 1492f2e35a3aSmrg 1493f2e35a3aSmrg for (yy = y_min; yy <= y_max; yy++) { 1494f2e35a3aSmrg const ColorRegister *scan = base + x_min; 1495f2e35a3aSmrg for (xx = x_min; xx <= x_max; xx++) { 1496f2e35a3aSmrg if (scan->r < 0 || scan->g < 0 || scan->b < 0) { 1497f2e35a3aSmrg holes++; 1498f2e35a3aSmrg } else { 1499f2e35a3aSmrg non_holes++; 1500f2e35a3aSmrg } 1501f2e35a3aSmrg ++scan; 150201037d57Smrg } 1503f2e35a3aSmrg base += refresh_w; 150401037d57Smrg } 150501037d57Smrg } 150601037d57Smrg 150701037d57Smrg if (non_holes < 1U) { 150801037d57Smrg TRACE(("refresh: visible graphics areas are erased; nothing to do\n")); 150901037d57Smrg free(buffer); 151001037d57Smrg return; 151101037d57Smrg } 151201037d57Smrg 151301037d57Smrg /* 151401037d57Smrg * If we have any holes we can't just copy an image rectangle, and masking 151501037d57Smrg * with bitmaps is very expensive. This fallback is surprisingly faster 151601037d57Smrg * than the XPutImage version in some cases, but I don't know why. 151701037d57Smrg * (This is even though there's no X11 primitive for drawing a horizontal 151801037d57Smrg * line of height one and no attempt is made to handle multiple lines at 151901037d57Smrg * once.) 152001037d57Smrg */ 152101037d57Smrg if (holes > 0U) { 152201037d57Smrg GC graphics_gc; 152301037d57Smrg XGCValues xgcv; 152401037d57Smrg ColorRegister last_color; 152501037d57Smrg ColorRegister gc_color; 152601037d57Smrg int run; 152701037d57Smrg 152801037d57Smrg memset(&xgcv, 0, sizeof(xgcv)); 152901037d57Smrg xgcv.graphics_exposures = False; 153001037d57Smrg graphics_gc = XCreateGC(display, drawable, GCGraphicsExposures, &xgcv); 153101037d57Smrg if (graphics_gc == None) { 153201037d57Smrg TRACE(("unable to allocate GC for graphics refresh\n")); 153301037d57Smrg free(buffer); 153401037d57Smrg return; 153501037d57Smrg } 153601037d57Smrg 1537f2e35a3aSmrg last_color = null_color; 1538f2e35a3aSmrg gc_color = null_color; 153901037d57Smrg run = 0; 154001037d57Smrg for (yy = draw_y_min - refresh_y; yy <= draw_y_max - refresh_y; yy++) { 154101037d57Smrg for (xx = draw_x_min - refresh_x; xx <= draw_x_max - refresh_x; 154201037d57Smrg xx++) { 154301037d57Smrg const ColorRegister color = buffer[yy * refresh_w + xx]; 154401037d57Smrg 154501037d57Smrg if (color.r < 0 || color.g < 0 || color.b < 0) { 154601037d57Smrg last_color = color; 154701037d57Smrg if (run > 0) { 154801037d57Smrg XDrawLine(display, drawable, graphics_gc, 154901037d57Smrg OriginX(screen) + refresh_x + xx - run, 155001037d57Smrg (OriginY(screen) - scroll_y) + refresh_y + yy, 155101037d57Smrg OriginX(screen) + refresh_x + xx - 1, 155201037d57Smrg (OriginY(screen) - scroll_y) + refresh_y + yy); 155301037d57Smrg run = 0; 155401037d57Smrg } 155501037d57Smrg continue; 155601037d57Smrg } 155701037d57Smrg 1558f2e35a3aSmrg if (DiffColor(color, last_color)) { 155901037d57Smrg last_color = color; 156001037d57Smrg if (run > 0) { 156101037d57Smrg XDrawLine(display, drawable, graphics_gc, 156201037d57Smrg OriginX(screen) + refresh_x + xx - run, 156301037d57Smrg (OriginY(screen) - scroll_y) + refresh_y + yy, 156401037d57Smrg OriginX(screen) + refresh_x + xx - 1, 156501037d57Smrg (OriginY(screen) - scroll_y) + refresh_y + yy); 156601037d57Smrg run = 0; 156701037d57Smrg } 156801037d57Smrg 1569f2e35a3aSmrg if (DiffColor(color, gc_color)) { 157001037d57Smrg xgcv.foreground = 157101037d57Smrg color_register_to_xpixel(&color, xw); 157201037d57Smrg XChangeGC(display, graphics_gc, GCForeground, &xgcv); 157301037d57Smrg gc_color = color; 157401037d57Smrg } 157501037d57Smrg } 157601037d57Smrg run++; 157701037d57Smrg } 157801037d57Smrg if (run > 0) { 1579f2e35a3aSmrg last_color = null_color; 158001037d57Smrg XDrawLine(display, drawable, graphics_gc, 158101037d57Smrg OriginX(screen) + refresh_x + xx - run, 158201037d57Smrg (OriginY(screen) - scroll_y) + refresh_y + yy, 158301037d57Smrg OriginX(screen) + refresh_x + xx - 1, 158401037d57Smrg (OriginY(screen) - scroll_y) + refresh_y + yy); 158501037d57Smrg run = 0; 158601037d57Smrg } 158701037d57Smrg } 158801037d57Smrg 158901037d57Smrg XFreeGC(display, graphics_gc); 159001037d57Smrg } else { 159101037d57Smrg XGCValues xgcv; 159201037d57Smrg GC graphics_gc; 1593f2e35a3aSmrg ColorRegister old_colors[2]; 1594f2e35a3aSmrg Pixel fg, old_result[2]; 159501037d57Smrg XImage *image; 159601037d57Smrg char *imgdata; 159701037d57Smrg unsigned image_w, image_h; 1598f2e35a3aSmrg int nn; 159901037d57Smrg 160001037d57Smrg memset(&xgcv, 0, sizeof(xgcv)); 160101037d57Smrg xgcv.graphics_exposures = False; 160201037d57Smrg graphics_gc = XCreateGC(display, drawable, GCGraphicsExposures, &xgcv); 160301037d57Smrg if (graphics_gc == None) { 160401037d57Smrg TRACE(("unable to allocate GC for graphics refresh\n")); 160501037d57Smrg free(buffer); 160601037d57Smrg return; 160701037d57Smrg } 160801037d57Smrg 160901037d57Smrg /* FIXME: is it worth reusing the GC/Image/imagedata across calls? */ 161001037d57Smrg /* FIXME: is it worth using shared memory when available? */ 161101037d57Smrg image_w = (unsigned) draw_x_max + 1U - (unsigned) draw_x_min; 161201037d57Smrg image_h = (unsigned) draw_y_max + 1U - (unsigned) draw_y_min; 161301037d57Smrg image = XCreateImage(display, xw->visInfo->visual, 161401037d57Smrg (unsigned) xw->visInfo->depth, 161501037d57Smrg ZPixmap, 0, NULL, 161601037d57Smrg image_w, image_h, 1617f2e35a3aSmrg (int) (sizeof(int) * 8U), 0); 161801037d57Smrg if (!image) { 161901037d57Smrg TRACE(("unable to allocate XImage for graphics refresh\n")); 162001037d57Smrg XFreeGC(display, graphics_gc); 162101037d57Smrg free(buffer); 162201037d57Smrg return; 162301037d57Smrg } 1624f2e35a3aSmrg imgdata = malloc((size_t) (image_h * (unsigned) image->bytes_per_line)); 162501037d57Smrg if (!imgdata) { 162601037d57Smrg TRACE(("unable to allocate XImage for graphics refresh\n")); 162701037d57Smrg XDestroyImage(image); 162801037d57Smrg XFreeGC(display, graphics_gc); 162901037d57Smrg free(buffer); 163001037d57Smrg return; 163101037d57Smrg } 163201037d57Smrg image->data = imgdata; 163301037d57Smrg 163401037d57Smrg fg = 0U; 1635f2e35a3aSmrg nn = 0; 1636f2e35a3aSmrg 1637f2e35a3aSmrg /* two-level cache cuts down on lookup-calls */ 1638f2e35a3aSmrg old_result[0] = 0U; 1639f2e35a3aSmrg old_result[1] = 0U; 1640f2e35a3aSmrg old_colors[0] = null_color; 1641f2e35a3aSmrg old_colors[1] = null_color; 1642f2e35a3aSmrg 164301037d57Smrg for (yy = draw_y_min - refresh_y; yy <= draw_y_max - refresh_y; yy++) { 164401037d57Smrg for (xx = draw_x_min - refresh_x; xx <= draw_x_max - refresh_x; 164501037d57Smrg xx++) { 164601037d57Smrg const ColorRegister color = buffer[yy * refresh_w + xx]; 164701037d57Smrg 1648f2e35a3aSmrg if (DiffColor(color, old_colors[nn])) { 1649f2e35a3aSmrg if (DiffColor(color, old_colors[!nn])) { 1650f2e35a3aSmrg nn = !nn; 1651f2e35a3aSmrg fg = color_register_to_xpixel(&color, xw); 1652f2e35a3aSmrg old_result[nn] = fg; 1653f2e35a3aSmrg old_colors[nn] = color; 1654f2e35a3aSmrg } else { 1655f2e35a3aSmrg nn = !nn; 1656f2e35a3aSmrg fg = old_result[nn]; 1657f2e35a3aSmrg } 165801037d57Smrg } 165901037d57Smrg 1660f2e35a3aSmrg XPutPixel(image, 1661f2e35a3aSmrg xx + refresh_x - draw_x_min, 166201037d57Smrg yy + refresh_y - draw_y_min, fg); 166301037d57Smrg } 166401037d57Smrg } 166501037d57Smrg 166601037d57Smrg XPutImage(display, drawable, graphics_gc, image, 166701037d57Smrg 0, 0, 166801037d57Smrg OriginX(screen) + draw_x_min, 166901037d57Smrg (OriginY(screen) - scroll_y) + draw_y_min, 167001037d57Smrg image_w, image_h); 167101037d57Smrg free(imgdata); 167201037d57Smrg image->data = NULL; 167301037d57Smrg XDestroyImage(image); 167401037d57Smrg XFreeGC(display, graphics_gc); 167501037d57Smrg } 167601037d57Smrg 167701037d57Smrg free(buffer); 167801037d57Smrg XFlush(display); 167901037d57Smrg} 168001037d57Smrg 168101037d57Smrgvoid 168201037d57Smrgrefresh_displayed_graphics(XtermWidget xw, 168301037d57Smrg int leftcol, 168401037d57Smrg int toprow, 168501037d57Smrg int ncols, 168601037d57Smrg int nrows) 168701037d57Smrg{ 168801037d57Smrg refresh_graphics(xw, leftcol, toprow, ncols, nrows, 0); 168901037d57Smrg} 169001037d57Smrg 169101037d57Smrgvoid 169201037d57Smrgrefresh_modified_displayed_graphics(XtermWidget xw) 169301037d57Smrg{ 169401037d57Smrg TScreen const *screen = TScreenOf(xw); 169501037d57Smrg refresh_graphics(xw, 0, 0, MaxCols(screen), MaxRows(screen), 1); 1696e0a2b6dfSmrg} 1697e0a2b6dfSmrg 1698894e0ac8Smrgvoid 169901037d57Smrgscroll_displayed_graphics(XtermWidget xw, int rows) 1700e0a2b6dfSmrg{ 1701f2e35a3aSmrg if (used_graphics) { 1702f2e35a3aSmrg TScreen const *screen = TScreenOf(xw); 1703f2e35a3aSmrg unsigned ii; 1704e0a2b6dfSmrg 1705f2e35a3aSmrg TRACE(("graphics scroll: moving all up %d rows\n", rows)); 1706f2e35a3aSmrg /* FIXME: VT125 ReGIS graphics are fixed at the upper left of the display; need to verify */ 1707894e0ac8Smrg 1708f2e35a3aSmrg FOR_EACH_SLOT(ii) { 1709f2e35a3aSmrg Graphic *graphic; 17102e4f8982Smrg 1711f2e35a3aSmrg if (!(graphic = getActiveSlot(ii))) 1712f2e35a3aSmrg continue; 1713f2e35a3aSmrg if (graphic->bufferid != screen->whichBuf) 1714f2e35a3aSmrg continue; 1715f2e35a3aSmrg if (graphic->hidden) 1716f2e35a3aSmrg continue; 1717e0a2b6dfSmrg 1718f2e35a3aSmrg graphic->charrow -= rows; 1719f2e35a3aSmrg } 1720e0a2b6dfSmrg } 1721e0a2b6dfSmrg} 1722e0a2b6dfSmrg 1723894e0ac8Smrgvoid 1724e0a2b6dfSmrgpixelarea_clear_displayed_graphics(TScreen const *screen, 1725e0a2b6dfSmrg int winx, 1726e0a2b6dfSmrg int winy, 1727e0a2b6dfSmrg int w, 1728e0a2b6dfSmrg int h) 1729e0a2b6dfSmrg{ 1730894e0ac8Smrg unsigned ii; 1731e0a2b6dfSmrg 1732f2e35a3aSmrg if (!used_graphics) 1733f2e35a3aSmrg return; 1734f2e35a3aSmrg 1735894e0ac8Smrg FOR_EACH_SLOT(ii) { 173601037d57Smrg Graphic *graphic; 173701037d57Smrg /* FIXME: are these coordinates (scrolled) screen-relative? */ 173801037d57Smrg int const scroll_y = (screen->whichBuf == 0 173901037d57Smrg ? screen->topline * FontHeight(screen) 174001037d57Smrg : 0); 174101037d57Smrg int graph_x; 174201037d57Smrg int graph_y; 174301037d57Smrg int x, y; 174401037d57Smrg 1745894e0ac8Smrg if (!(graphic = getActiveSlot(ii))) 1746e0a2b6dfSmrg continue; 174701037d57Smrg if (graphic->bufferid != screen->whichBuf) 174801037d57Smrg continue; 17492e4f8982Smrg if (graphic->hidden) 17502e4f8982Smrg continue; 1751e0a2b6dfSmrg 175201037d57Smrg graph_x = graphic->charcol * FontWidth(screen); 175301037d57Smrg graph_y = graphic->charrow * FontHeight(screen); 175401037d57Smrg x = winx - graph_x; 175501037d57Smrg y = (winy - scroll_y) - graph_y; 1756e0a2b6dfSmrg 175701037d57Smrg TRACE(("pixelarea clear graphics: screen->topline=%d winx=%d winy=%d w=%d h=%d x=%d y=%d\n", 1758e0a2b6dfSmrg screen->topline, 1759e0a2b6dfSmrg winx, winy, 1760e0a2b6dfSmrg w, h, 1761e0a2b6dfSmrg x, y)); 1762894e0ac8Smrg erase_graphic(graphic, x, y, w, h); 1763e0a2b6dfSmrg } 1764e0a2b6dfSmrg} 1765e0a2b6dfSmrg 1766894e0ac8Smrgvoid 1767e0a2b6dfSmrgchararea_clear_displayed_graphics(TScreen const *screen, 1768e0a2b6dfSmrg int leftcol, 1769e0a2b6dfSmrg int toprow, 1770e0a2b6dfSmrg int ncols, 1771e0a2b6dfSmrg int nrows) 1772e0a2b6dfSmrg{ 1773f2e35a3aSmrg if (used_graphics) { 1774f2e35a3aSmrg int const x = leftcol * FontWidth(screen); 1775f2e35a3aSmrg int const y = toprow * FontHeight(screen); 1776f2e35a3aSmrg int const w = ncols * FontWidth(screen); 1777f2e35a3aSmrg int const h = nrows * FontHeight(screen); 1778e0a2b6dfSmrg 1779f2e35a3aSmrg TRACE(("chararea clear graphics: screen->topline=%d leftcol=%d toprow=%d nrows=%d ncols=%d x=%d y=%d w=%d h=%d\n", 1780f2e35a3aSmrg screen->topline, 1781f2e35a3aSmrg leftcol, toprow, 1782f2e35a3aSmrg nrows, ncols, 1783f2e35a3aSmrg x, y, w, h)); 1784f2e35a3aSmrg pixelarea_clear_displayed_graphics(screen, x, y, w, h); 1785f2e35a3aSmrg } 1786e0a2b6dfSmrg} 1787e0a2b6dfSmrg 1788894e0ac8Smrgvoid 1789e0a2b6dfSmrgreset_displayed_graphics(TScreen const *screen) 1790e0a2b6dfSmrg{ 1791f2e35a3aSmrg init_color_registers(getSharedRegisters(), GraphicsTermId(screen)); 1792894e0ac8Smrg 1793f2e35a3aSmrg if (used_graphics) { 1794f2e35a3aSmrg unsigned ii; 1795894e0ac8Smrg 1796f2e35a3aSmrg TRACE(("resetting all graphics\n")); 1797f2e35a3aSmrg FOR_EACH_SLOT(ii) { 1798f2e35a3aSmrg deactivateSlot(ii); 1799f2e35a3aSmrg } 1800894e0ac8Smrg } 1801894e0ac8Smrg} 1802894e0ac8Smrg 1803894e0ac8Smrg#ifdef NO_LEAKS 1804894e0ac8Smrgvoid 1805894e0ac8Smrgnoleaks_graphics(void) 1806894e0ac8Smrg{ 1807894e0ac8Smrg unsigned ii; 1808894e0ac8Smrg 1809894e0ac8Smrg FOR_EACH_SLOT(ii) { 1810894e0ac8Smrg deactivateSlot(ii); 1811e0a2b6dfSmrg } 1812e0a2b6dfSmrg} 1813894e0ac8Smrg#endif 1814