graphics.c revision 2e4f8982
12e4f8982Smrg/* $XTermId: graphics.c,v 1.69 2016/05/17 10:04:40 tom Exp $ */ 2e0a2b6dfSmrg 3e0a2b6dfSmrg/* 42e4f8982Smrg * Copyright 2013-2015,2016 by Ross Combs 5e0a2b6dfSmrg * 6e0a2b6dfSmrg * All Rights Reserved 7e0a2b6dfSmrg * 8e0a2b6dfSmrg * Permission is hereby granted, free of charge, to any person obtaining a 9e0a2b6dfSmrg * copy of this software and associated documentation files (the 10e0a2b6dfSmrg * "Software"), to deal in the Software without restriction, including 11e0a2b6dfSmrg * without limitation the rights to use, copy, modify, merge, publish, 12e0a2b6dfSmrg * distribute, sublicense, and/or sell copies of the Software, and to 13e0a2b6dfSmrg * permit persons to whom the Software is furnished to do so, subject to 14e0a2b6dfSmrg * the following conditions: 15e0a2b6dfSmrg * 16e0a2b6dfSmrg * The above copyright notice and this permission notice shall be included 17e0a2b6dfSmrg * in all copies or substantial portions of the Software. 18e0a2b6dfSmrg * 19e0a2b6dfSmrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 20e0a2b6dfSmrg * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21e0a2b6dfSmrg * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 22e0a2b6dfSmrg * IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY 23e0a2b6dfSmrg * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 24e0a2b6dfSmrg * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 25e0a2b6dfSmrg * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 26e0a2b6dfSmrg * 27e0a2b6dfSmrg * Except as contained in this notice, the name(s) of the above copyright 28e0a2b6dfSmrg * holders shall not be used in advertising or otherwise to promote the 29e0a2b6dfSmrg * sale, use or other dealings in this Software without prior written 30e0a2b6dfSmrg * authorization. 31e0a2b6dfSmrg */ 32e0a2b6dfSmrg 33e0a2b6dfSmrg#include <xterm.h> 34e0a2b6dfSmrg 35e0a2b6dfSmrg#include <stdio.h> 36e0a2b6dfSmrg#include <ctype.h> 37e0a2b6dfSmrg#include <stdlib.h> 38e0a2b6dfSmrg 39e0a2b6dfSmrg#include <data.h> 40e0a2b6dfSmrg#include <ptyx.h> 41e0a2b6dfSmrg 42e0a2b6dfSmrg#include <assert.h> 43e0a2b6dfSmrg#include <graphics.h> 44e0a2b6dfSmrg 45894e0ac8Smrg#undef DUMP_BITMAP 46894e0ac8Smrg#undef DUMP_COLORS 47894e0ac8Smrg#undef DEBUG_PALETTE 48894e0ac8Smrg#undef DEBUG_PIXEL 49e0a2b6dfSmrg#undef DEBUG_REFRESH 50e0a2b6dfSmrg 5101037d57Smrg/* 5201037d57Smrg * graphics TODO list 5301037d57Smrg * 54e0a2b6dfSmrg * ReGIS: 552e4f8982Smrg * - ship a default alphabet zero font instead of scaling Xft fonts 56894e0ac8Smrg * - input and output cursors 57894e0ac8Smrg * - mouse input 582e4f8982Smrg * - fix graphic pages for ReGIS -- they should also apply to text and sixel graphics 5901037d57Smrg * - fix interpolated curves to more closely match implementation (identical despite direction and starting point) 6001037d57Smrg * - non-ASCII alphabets 612e4f8982Smrg * - enter/leave anywhere in a command 622e4f8982Smrg * - locator key definitions (DECLKD) 63894e0ac8Smrg * - command display mode 6401037d57Smrg * - re-rasterization on resize 65894e0ac8Smrg * - macros 6601037d57Smrg * - improved fills for narrow angles (track actual lines not just pixels) 6701037d57Smrg * 68e0a2b6dfSmrg * sixel: 69894e0ac8Smrg * - fix problem where new_row < 0 during sixel parsing (see FIXME) 7001037d57Smrg * - screen-capture support (need dialog of some sort for safety) 7101037d57Smrg * 72894e0ac8Smrg * VT55/VT105 waveform graphics 73894e0ac8Smrg * - everything 7401037d57Smrg * 7501037d57Smrg * Tektronix: 7601037d57Smrg * - color (VT340 4014 emulation, 41xx, IRAF GTERM, and also MS-DOS Kermit color support) 7701037d57Smrg * - polygon fill (41xx) 7801037d57Smrg * - clear area extension 7901037d57Smrg * - area fill extension 8001037d57Smrg * - pixel operations (RU/RS/RP) 8101037d57Smrg * - research other 41xx and 42xx extensions 8201037d57Smrg * 8301037d57Smrg * common graphics features: 84894e0ac8Smrg * - handle light/dark screen modes (CSI?5[hl]) 85894e0ac8Smrg * - update text fg/bg color which overlaps images 8601037d57Smrg * - handle graphic updates in scroll regions (verify effect on graphics) 87894e0ac8Smrg * - handle rectangular area copies (verify they work with graphics) 8801037d57Smrg * - invalidate graphics under graphic if same origin, at least as big, and bg not transparent 8901037d57Smrg * - invalidate graphic if completely scrolled past end of scrollback 9001037d57Smrg * - invalidate graphic if all pixels are transparent/erased 9101037d57Smrg * - invalidate graphic if completely scrolled out of alt buffer 9201037d57Smrg * - posturize requested colors to match hardware palettes (e.g. only four possible shades on VT240) 93894e0ac8Smrg * - color register report/restore 9401037d57Smrg * - ability to select/copy graphics for pasting in other programs 9501037d57Smrg * - ability to show non-scroll-mode sixel graphics in a separate window 9601037d57Smrg * - ability to show ReGIS graphics in a separate window 9701037d57Smrg * - ability to show Tektronix graphics in VT100 window 982e4f8982Smrg * - truncate graphics at bottom edge of terminal? 992e4f8982Smrg * - locator events (DECEFR DECSLE DECELR DECLRP) 1002e4f8982Smrg * - locator controller mode (CSI6i / CSI7i) 10101037d57Smrg * 10201037d57Smrg * new escape sequences: 10301037d57Smrg * - way to query text font size without "window ops" (or make "window ops" permissions more fine grained) 104894e0ac8Smrg * - way to query and set the number of graphics pages 10501037d57Smrg * 106894e0ac8Smrg * ReGIS extensions: 10701037d57Smrg * - non-integer text scaling 10801037d57Smrg * - free distortionless text rotation 10901037d57Smrg * - font characteristics: bold/underline/italic 11001037d57Smrg * - remove/increase arbitrary limits (pattern size, pages, alphabets, stack size, font names, etc.) 11101037d57Smrg * - comment command 11201037d57Smrg * - shade/fill with borders 11301037d57Smrg * - sprites (copy portion of page into/out of buffer with scaling and rotation) 11401037d57Smrg * - ellipses 11501037d57Smrg * - 2D patterns 11601037d57Smrg * - option to set actual graphic size (not just coordinate range) 11701037d57Smrg * - gradients (for lines and fills) 118894e0ac8Smrg * - line width (RLogin has this and it is mentioned in docs for the DEC ReGIS to Postscript converter) 119894e0ac8Smrg * - transparency 120894e0ac8Smrg * - background color as stackable write control 121894e0ac8Smrg * - true color (virtual color registers created upon lookup) 122894e0ac8Smrg * - anti-aliasing 12301037d57Smrg * - variable-width text 124e0a2b6dfSmrg */ 125e0a2b6dfSmrg 126e0a2b6dfSmrg/* font sizes: 127e0a2b6dfSmrg * VT510: 128e0a2b6dfSmrg * 80 Columns 132 Columns Maximum Number of Lines 129e0a2b6dfSmrg * 10 x 16 6 x 16 26 lines + keyboard indicator line 130894e0ac8Smrg * 10 x 13 6 x 13 26 lines + keyboard indicator line 131e0a2b6dfSmrg * 10 x 10 6 x 10 42 lines + keyboard indicator line 132e0a2b6dfSmrg * 10 x 8 6 x 8 53 lines + keyboard indicator line 13301037d57Smrg */ 13401037d57Smrg 13501037d57Smrgtypedef struct allocated_color_register { 13601037d57Smrg struct allocated_color_register *next; 13701037d57Smrg Pixel pix; 13801037d57Smrg short r, g, b; 13901037d57Smrg} AllocatedColorRegister; 14001037d57Smrg 14101037d57Smrg#define LOOKUP_WIDTH 16 14201037d57Smrgstatic AllocatedColorRegister *allocated_colors[LOOKUP_WIDTH][LOOKUP_WIDTH][LOOKUP_WIDTH]; 143e0a2b6dfSmrg 144894e0ac8Smrg#define FOR_EACH_SLOT(ii) for (ii = 0U; ii < MAX_GRAPHICS; ii++) 145894e0ac8Smrg 146894e0ac8Smrgstatic ColorRegister *shared_color_registers; 147894e0ac8Smrgstatic Graphic *displayed_graphics[MAX_GRAPHICS]; 148894e0ac8Smrgstatic unsigned next_graphic_id = 0U; 149894e0ac8Smrg 150894e0ac8Smrgstatic ColorRegister * 151894e0ac8SmrgallocRegisters(void) 152e0a2b6dfSmrg{ 153894e0ac8Smrg return TypeCallocN(ColorRegister, MAX_COLOR_REGISTERS); 154894e0ac8Smrg} 155894e0ac8Smrg 156894e0ac8Smrgstatic Graphic * 157894e0ac8SmrgfreeGraphic(Graphic *obj) 158894e0ac8Smrg{ 159894e0ac8Smrg if (obj) { 160894e0ac8Smrg if (obj->pixels) 161894e0ac8Smrg free(obj->pixels); 162894e0ac8Smrg if (obj->private_color_registers) 163894e0ac8Smrg free(obj->private_color_registers); 164894e0ac8Smrg free(obj); 165894e0ac8Smrg } 166894e0ac8Smrg return NULL; 167894e0ac8Smrg} 168894e0ac8Smrg 169894e0ac8Smrgstatic Graphic * 17001037d57SmrgallocGraphic(int max_w, int max_h) 171894e0ac8Smrg{ 172894e0ac8Smrg Graphic *result = TypeCalloc(Graphic); 173894e0ac8Smrg if (result) { 17401037d57Smrg result->max_width = max_w; 17501037d57Smrg result->max_height = max_h; 17601037d57Smrg if (!(result->pixels = TypeCallocN(RegisterNum, 17701037d57Smrg (size_t) max_w * (size_t) max_h))) { 178894e0ac8Smrg result = freeGraphic(result); 179894e0ac8Smrg } else if (!(result->private_color_registers = allocRegisters())) { 180894e0ac8Smrg result = freeGraphic(result); 181e0a2b6dfSmrg } 182e0a2b6dfSmrg } 183894e0ac8Smrg return result; 184894e0ac8Smrg} 185e0a2b6dfSmrg 186894e0ac8Smrgstatic Graphic * 187894e0ac8SmrggetActiveSlot(unsigned n) 188894e0ac8Smrg{ 189894e0ac8Smrg if (n < MAX_GRAPHICS && 190894e0ac8Smrg displayed_graphics[n] && 191894e0ac8Smrg displayed_graphics[n]->valid) { 192894e0ac8Smrg return displayed_graphics[n]; 193894e0ac8Smrg } 194894e0ac8Smrg return NULL; 195e0a2b6dfSmrg} 196e0a2b6dfSmrg 197894e0ac8Smrgstatic Graphic * 19801037d57SmrggetInactiveSlot(const TScreen *screen, unsigned n) 199894e0ac8Smrg{ 200894e0ac8Smrg if (n < MAX_GRAPHICS && 201894e0ac8Smrg (!displayed_graphics[n] || 202894e0ac8Smrg !displayed_graphics[n]->valid)) { 203894e0ac8Smrg if (!displayed_graphics[n]) { 20401037d57Smrg displayed_graphics[n] = allocGraphic(screen->graphics_max_wide, 20501037d57Smrg screen->graphics_max_high); 206894e0ac8Smrg } 207894e0ac8Smrg return displayed_graphics[n]; 208894e0ac8Smrg } 209894e0ac8Smrg return NULL; 210894e0ac8Smrg} 211894e0ac8Smrg 212894e0ac8Smrgstatic ColorRegister * 213894e0ac8SmrggetSharedRegisters(void) 214894e0ac8Smrg{ 215894e0ac8Smrg if (!shared_color_registers) 216894e0ac8Smrg shared_color_registers = allocRegisters(); 217894e0ac8Smrg return shared_color_registers; 218894e0ac8Smrg} 219e0a2b6dfSmrg 220e0a2b6dfSmrgstatic void 221894e0ac8SmrgdeactivateSlot(unsigned n) 222e0a2b6dfSmrg{ 223894e0ac8Smrg if (n < MAX_GRAPHICS) { 224894e0ac8Smrg displayed_graphics[n] = freeGraphic(displayed_graphics[n]); 225894e0ac8Smrg } 226894e0ac8Smrg} 227e0a2b6dfSmrg 228894e0ac8Smrgextern RegisterNum 229894e0ac8Smrgread_pixel(Graphic *graphic, int x, int y) 230894e0ac8Smrg{ 23101037d57Smrg if (x < 0 || x >= graphic->actual_width || 23201037d57Smrg y < 0 || y >= graphic->actual_height) { 233894e0ac8Smrg return COLOR_HOLE; 234e0a2b6dfSmrg } 235894e0ac8Smrg 236894e0ac8Smrg return graphic->pixels[y * graphic->max_width + x]; 237e0a2b6dfSmrg} 238e0a2b6dfSmrg 23901037d57Smrg#define _draw_pixel(G, X, Y, C) \ 24001037d57Smrg do { \ 24101037d57Smrg (G)->pixels[(Y) * (G)->max_width + (X)] = (RegisterNum) (C); \ 24201037d57Smrg } while (0) 24301037d57Smrg 244894e0ac8Smrgvoid 245894e0ac8Smrgdraw_solid_pixel(Graphic *graphic, int x, int y, unsigned color) 246e0a2b6dfSmrg{ 247894e0ac8Smrg assert(color <= MAX_COLOR_REGISTERS); 248e0a2b6dfSmrg 249894e0ac8Smrg#ifdef DEBUG_PIXEL 250894e0ac8Smrg TRACE(("drawing pixel at %d,%d color=%hu (hole=%hu, [%d,%d,%d])\n", 251894e0ac8Smrg x, 252894e0ac8Smrg y, 253e0a2b6dfSmrg color, 254894e0ac8Smrg COLOR_HOLE, 255e0a2b6dfSmrg ((color != COLOR_HOLE) 256894e0ac8Smrg ? (unsigned) graphic->color_registers[color].r : 0U), 257e0a2b6dfSmrg ((color != COLOR_HOLE) 258894e0ac8Smrg ? (unsigned) graphic->color_registers[color].g : 0U), 259e0a2b6dfSmrg ((color != COLOR_HOLE) 260894e0ac8Smrg ? (unsigned) graphic->color_registers[color].b : 0U))); 261894e0ac8Smrg#endif 262894e0ac8Smrg if (x >= 0 && x < graphic->actual_width && 263894e0ac8Smrg y >= 0 && y < graphic->actual_height) { 26401037d57Smrg _draw_pixel(graphic, x, y, color); 265894e0ac8Smrg if (color < MAX_COLOR_REGISTERS) 266894e0ac8Smrg graphic->color_registers_used[color] = 1; 267894e0ac8Smrg } 268894e0ac8Smrg} 269894e0ac8Smrg 270894e0ac8Smrgvoid 271894e0ac8Smrgdraw_solid_rectangle(Graphic *graphic, int x1, int y1, int x2, int y2, unsigned color) 272894e0ac8Smrg{ 273894e0ac8Smrg int x, y; 274894e0ac8Smrg int tmp; 275894e0ac8Smrg 276894e0ac8Smrg assert(color <= MAX_COLOR_REGISTERS); 277894e0ac8Smrg 278894e0ac8Smrg if (x1 > x2) { 279894e0ac8Smrg EXCHANGE(x1, x2, tmp); 280894e0ac8Smrg } 281894e0ac8Smrg if (y1 > y2) { 282894e0ac8Smrg EXCHANGE(y1, y2, tmp); 283894e0ac8Smrg } 284894e0ac8Smrg 28501037d57Smrg if (x2 < 0 || x1 >= graphic->actual_width || 28601037d57Smrg y2 < 0 || y1 >= graphic->actual_height) 28701037d57Smrg return; 28801037d57Smrg 28901037d57Smrg if (x1 < 0) 29001037d57Smrg x1 = 0; 29101037d57Smrg if (x2 >= graphic->actual_width) 29201037d57Smrg x2 = graphic->actual_width - 1; 29301037d57Smrg if (y1 < 0) 29401037d57Smrg y1 = 0; 29501037d57Smrg if (y2 >= graphic->actual_height) 29601037d57Smrg y2 = graphic->actual_height - 1; 29701037d57Smrg 29801037d57Smrg if (color < MAX_COLOR_REGISTERS) 29901037d57Smrg graphic->color_registers_used[color] = 1; 300894e0ac8Smrg for (y = y1; y <= y2; y++) 30101037d57Smrg for (x = x1; x <= x2; x++) 30201037d57Smrg _draw_pixel(graphic, x, y, color); 303894e0ac8Smrg} 304894e0ac8Smrg 305894e0ac8Smrgvoid 306894e0ac8Smrgdraw_solid_line(Graphic *graphic, int x1, int y1, int x2, int y2, unsigned color) 307894e0ac8Smrg{ 308894e0ac8Smrg int x, y; 309894e0ac8Smrg int dx, dy; 310894e0ac8Smrg int dir, diff; 311894e0ac8Smrg 312894e0ac8Smrg assert(color <= MAX_COLOR_REGISTERS); 313894e0ac8Smrg 314894e0ac8Smrg dx = abs(x1 - x2); 315894e0ac8Smrg dy = abs(y1 - y2); 316894e0ac8Smrg 317894e0ac8Smrg if (dx > dy) { 318894e0ac8Smrg if (x1 > x2) { 319894e0ac8Smrg int tmp; 320894e0ac8Smrg EXCHANGE(x1, x2, tmp); 321894e0ac8Smrg EXCHANGE(y1, y2, tmp); 322894e0ac8Smrg } 323894e0ac8Smrg if (y1 < y2) 324894e0ac8Smrg dir = 1; 325894e0ac8Smrg else if (y1 > y2) 326894e0ac8Smrg dir = -1; 327894e0ac8Smrg else 328894e0ac8Smrg dir = 0; 329894e0ac8Smrg 330894e0ac8Smrg diff = 0; 331894e0ac8Smrg y = y1; 332894e0ac8Smrg for (x = x1; x <= x2; x++) { 333894e0ac8Smrg if (diff >= dx) { 334894e0ac8Smrg diff -= dx; 335894e0ac8Smrg y += dir; 336e0a2b6dfSmrg } 337894e0ac8Smrg diff += dy; 338894e0ac8Smrg draw_solid_pixel(graphic, x, y, color); 339894e0ac8Smrg } 340894e0ac8Smrg } else { 341894e0ac8Smrg if (y1 > y2) { 342894e0ac8Smrg int tmp; 343894e0ac8Smrg EXCHANGE(x1, x2, tmp); 344894e0ac8Smrg EXCHANGE(y1, y2, tmp); 345894e0ac8Smrg } 346894e0ac8Smrg if (x1 < x2) 347894e0ac8Smrg dir = 1; 348894e0ac8Smrg else if (x1 > x2) 349894e0ac8Smrg dir = -1; 350894e0ac8Smrg else 351894e0ac8Smrg dir = 0; 352894e0ac8Smrg 353894e0ac8Smrg diff = 0; 354894e0ac8Smrg x = x1; 355894e0ac8Smrg for (y = y1; y <= y2; y++) { 356894e0ac8Smrg if (diff >= dy) { 357894e0ac8Smrg diff -= dy; 358894e0ac8Smrg x += dir; 359894e0ac8Smrg } 360894e0ac8Smrg diff += dx; 361894e0ac8Smrg draw_solid_pixel(graphic, x, y, color); 362e0a2b6dfSmrg } 363e0a2b6dfSmrg } 364e0a2b6dfSmrg} 365e0a2b6dfSmrg 36601037d57Smrgvoid 36701037d57Smrgcopy_overlapping_area(Graphic *graphic, int src_ul_x, int src_ul_y, 36801037d57Smrg int dst_ul_x, int dst_ul_y, unsigned w, unsigned h, 36901037d57Smrg unsigned default_color) 37001037d57Smrg{ 37101037d57Smrg int sx, ex, dx; 37201037d57Smrg int sy, ey, dy; 37301037d57Smrg int xx, yy; 37401037d57Smrg RegisterNum color; 37501037d57Smrg 37601037d57Smrg if (dst_ul_x <= src_ul_x) { 37701037d57Smrg sx = 0; 37801037d57Smrg ex = (int) w - 1; 37901037d57Smrg dx = +1; 38001037d57Smrg } else { 38101037d57Smrg sx = (int) w - 1; 38201037d57Smrg ex = 0; 38301037d57Smrg dx = -1; 38401037d57Smrg } 38501037d57Smrg 38601037d57Smrg if (dst_ul_y <= src_ul_y) { 38701037d57Smrg sy = 0; 38801037d57Smrg ey = (int) h - 1; 38901037d57Smrg dy = +1; 39001037d57Smrg } else { 39101037d57Smrg sy = (int) h - 1; 39201037d57Smrg ey = 0; 39301037d57Smrg dy = -1; 39401037d57Smrg } 39501037d57Smrg 39601037d57Smrg for (yy = sy; yy != ey + dy; yy += dy) { 3972e4f8982Smrg int dst_y = dst_ul_y + yy; 3982e4f8982Smrg int src_y = src_ul_y + yy; 39901037d57Smrg if (dst_y < 0 || dst_y >= (int) graphic->actual_height) 40001037d57Smrg continue; 40101037d57Smrg 40201037d57Smrg for (xx = sx; xx != ex + dx; xx += dx) { 4032e4f8982Smrg int dst_x = dst_ul_x + xx; 4042e4f8982Smrg int src_x = src_ul_x + xx; 40501037d57Smrg if (dst_x < 0 || dst_x >= (int) graphic->actual_width) 40601037d57Smrg continue; 40701037d57Smrg 40801037d57Smrg if (src_x < 0 || src_x >= (int) graphic->actual_width || 40901037d57Smrg src_y < 0 || src_y >= (int) graphic->actual_height) 41001037d57Smrg color = (RegisterNum) default_color; 41101037d57Smrg else 41201037d57Smrg color = graphic->pixels[(unsigned) (src_y * 41301037d57Smrg graphic->max_width) + 41401037d57Smrg (unsigned) src_x]; 41501037d57Smrg 41601037d57Smrg graphic->pixels[(unsigned) (dst_y * graphic->max_width) + 41701037d57Smrg (unsigned) dst_x] = color; 41801037d57Smrg } 41901037d57Smrg } 42001037d57Smrg} 42101037d57Smrg 422e0a2b6dfSmrgstatic void 423894e0ac8Smrgset_color_register(ColorRegister *color_registers, 424894e0ac8Smrg unsigned color, 425894e0ac8Smrg int r, 426894e0ac8Smrg int g, 427894e0ac8Smrg int b) 428e0a2b6dfSmrg{ 429e0a2b6dfSmrg ColorRegister *reg = &color_registers[color]; 430e0a2b6dfSmrg reg->r = (short) r; 431e0a2b6dfSmrg reg->g = (short) g; 432e0a2b6dfSmrg reg->b = (short) b; 433e0a2b6dfSmrg} 434e0a2b6dfSmrg 435894e0ac8Smrg/* Graphics which don't use private colors will act as if they are using a 436894e0ac8Smrg * device-wide color palette. 437894e0ac8Smrg */ 438894e0ac8Smrgstatic void 439894e0ac8Smrgset_shared_color_register(unsigned color, int r, int g, int b) 440894e0ac8Smrg{ 441894e0ac8Smrg unsigned ii; 442894e0ac8Smrg 443894e0ac8Smrg assert(color < MAX_COLOR_REGISTERS); 444894e0ac8Smrg 445894e0ac8Smrg set_color_register(getSharedRegisters(), color, r, g, b); 446894e0ac8Smrg 447894e0ac8Smrg FOR_EACH_SLOT(ii) { 4482e4f8982Smrg Graphic *graphic; 4492e4f8982Smrg 450894e0ac8Smrg if (!(graphic = getActiveSlot(ii))) 451894e0ac8Smrg continue; 452894e0ac8Smrg if (graphic->private_colors) 453894e0ac8Smrg continue; 454894e0ac8Smrg 455894e0ac8Smrg if (graphic->color_registers_used[ii]) { 456894e0ac8Smrg graphic->dirty = 1; 457894e0ac8Smrg } 458894e0ac8Smrg } 459894e0ac8Smrg} 460894e0ac8Smrg 461894e0ac8Smrgvoid 462894e0ac8Smrgupdate_color_register(Graphic *graphic, 463894e0ac8Smrg unsigned color, 464894e0ac8Smrg int r, 465894e0ac8Smrg int g, 466894e0ac8Smrg int b) 467894e0ac8Smrg{ 468894e0ac8Smrg assert(color < MAX_COLOR_REGISTERS); 469894e0ac8Smrg 470894e0ac8Smrg if (graphic->private_colors) { 471894e0ac8Smrg set_color_register(graphic->private_color_registers, 472894e0ac8Smrg color, r, g, b); 473894e0ac8Smrg if (graphic->color_registers_used[color]) { 474894e0ac8Smrg graphic->dirty = 1; 475894e0ac8Smrg } 476894e0ac8Smrg graphic->color_registers_used[color] = 1; 477894e0ac8Smrg } else { 478894e0ac8Smrg set_shared_color_register(color, r, g, b); 479894e0ac8Smrg } 480894e0ac8Smrg} 481894e0ac8Smrg 482894e0ac8Smrg#define SQUARE(X) ( (X) * (X) ) 483894e0ac8Smrg 484894e0ac8SmrgRegisterNum 485894e0ac8Smrgfind_color_register(ColorRegister const *color_registers, int r, int g, int b) 486894e0ac8Smrg{ 487894e0ac8Smrg unsigned i; 488894e0ac8Smrg unsigned closest_index; 489894e0ac8Smrg unsigned closest_distance; 490894e0ac8Smrg 491894e0ac8Smrg /* I have no idea what algorithm DEC used for this. 492894e0ac8Smrg * The documentation warns that it is unpredictable, especially with values 493894e0ac8Smrg * far away from any allocated color so it is probably a very simple 49401037d57Smrg * heuristic rather than something fancy like finding the minimum distance 495894e0ac8Smrg * in a linear perceptive color space. 496894e0ac8Smrg */ 497894e0ac8Smrg closest_index = MAX_COLOR_REGISTERS; 498894e0ac8Smrg closest_distance = 0U; 499894e0ac8Smrg for (i = 0U; i < MAX_COLOR_REGISTERS; i++) { 5002e4f8982Smrg unsigned d = (unsigned) (SQUARE(2 * (color_registers[i].r - r)) + 5012e4f8982Smrg SQUARE(3 * (color_registers[i].g - g)) + 5022e4f8982Smrg SQUARE(1 * (color_registers[i].b - b))); 503894e0ac8Smrg if (closest_index == MAX_COLOR_REGISTERS || d < closest_distance) { 504894e0ac8Smrg closest_index = i; 505894e0ac8Smrg closest_distance = d; 506894e0ac8Smrg } 507894e0ac8Smrg } 508894e0ac8Smrg 509894e0ac8Smrg TRACE(("found closest color register to %d,%d,%d: %u (distance %u value %d,%d,%d)\n", 510894e0ac8Smrg r, g, b, 511894e0ac8Smrg closest_index, 512894e0ac8Smrg closest_distance, 513894e0ac8Smrg color_registers[closest_index].r, 514894e0ac8Smrg color_registers[closest_index].g, 515894e0ac8Smrg color_registers[closest_index].b)); 516894e0ac8Smrg return (RegisterNum) closest_index; 517894e0ac8Smrg} 518894e0ac8Smrg 519e0a2b6dfSmrgstatic void 520e0a2b6dfSmrginit_color_registers(ColorRegister *color_registers, int terminal_id) 521e0a2b6dfSmrg{ 522894e0ac8Smrg TRACE(("setting initial colors for terminal %d\n", terminal_id)); 523e0a2b6dfSmrg { 524894e0ac8Smrg unsigned i; 525e0a2b6dfSmrg 526e0a2b6dfSmrg for (i = 0U; i < MAX_COLOR_REGISTERS; i++) { 527894e0ac8Smrg set_color_register(color_registers, (RegisterNum) i, 0, 0, 0); 528e0a2b6dfSmrg } 529e0a2b6dfSmrg } 530e0a2b6dfSmrg 531e0a2b6dfSmrg /* 532e0a2b6dfSmrg * default color registers: 533e0a2b6dfSmrg * (mono) (color) 534e0a2b6dfSmrg * VK100/GIGI (fixed) 535e0a2b6dfSmrg * VT125: 536e0a2b6dfSmrg * 0: 0% 0% 537e0a2b6dfSmrg * 1: 33% blue 538e0a2b6dfSmrg * 2: 66% red 539e0a2b6dfSmrg * 3: 100% green 540e0a2b6dfSmrg * VT240: 541e0a2b6dfSmrg * 0: 0% 0% 542e0a2b6dfSmrg * 1: 33% blue 543e0a2b6dfSmrg * 2: 66% red 544e0a2b6dfSmrg * 3: 100% green 545e0a2b6dfSmrg * VT241: 546e0a2b6dfSmrg * 0: 0% 0% 547e0a2b6dfSmrg * 1: 33% blue 548e0a2b6dfSmrg * 2: 66% red 549e0a2b6dfSmrg * 3: 100% green 550e0a2b6dfSmrg * VT330: 551e0a2b6dfSmrg * 0: 0% 0% (bg for light on dark mode) 552e0a2b6dfSmrg * 1: 33% blue (red?) 553e0a2b6dfSmrg * 2: 66% red (green?) 554e0a2b6dfSmrg * 3: 100% green (yellow?) (fg for light on dark mode) 555e0a2b6dfSmrg * VT340: 556e0a2b6dfSmrg * 0: 0% 0% (bg for light on dark mode) 557e0a2b6dfSmrg * 1: 14% blue 558e0a2b6dfSmrg * 2: 29% red 559e0a2b6dfSmrg * 3: 43% green 560e0a2b6dfSmrg * 4: 57% magenta 561e0a2b6dfSmrg * 5: 71% cyan 562e0a2b6dfSmrg * 6: 86% yellow 563e0a2b6dfSmrg * 7: 100% 50% (fg for light on dark mode) 564e0a2b6dfSmrg * 8: 0% 25% 565e0a2b6dfSmrg * 9: 14% gray-blue 566e0a2b6dfSmrg * 10: 29% gray-red 567e0a2b6dfSmrg * 11: 43% gray-green 568e0a2b6dfSmrg * 12: 57% gray-magenta 569e0a2b6dfSmrg * 13: 71% gray-cyan 570e0a2b6dfSmrg * 14: 86% gray-yellow 571894e0ac8Smrg * 15: 100% 75% ("white") 572894e0ac8Smrg * VT382: 573894e0ac8Smrg * ? (FIXME: B&W only?) 574e0a2b6dfSmrg * dxterm: 575e0a2b6dfSmrg * ? 576e0a2b6dfSmrg */ 577e0a2b6dfSmrg switch (terminal_id) { 578e0a2b6dfSmrg case 125: 579e0a2b6dfSmrg case 241: 580894e0ac8Smrg set_color_register(color_registers, 0, 0, 0, 0); 581894e0ac8Smrg set_color_register(color_registers, 1, 0, 0, 100); 582894e0ac8Smrg set_color_register(color_registers, 2, 0, 100, 0); 583894e0ac8Smrg set_color_register(color_registers, 3, 100, 0, 0); 584e0a2b6dfSmrg break; 585e0a2b6dfSmrg case 240: 586e0a2b6dfSmrg case 330: 587894e0ac8Smrg set_color_register(color_registers, 0, 0, 0, 0); 588894e0ac8Smrg set_color_register(color_registers, 1, 33, 33, 33); 589894e0ac8Smrg set_color_register(color_registers, 2, 66, 66, 66); 590894e0ac8Smrg set_color_register(color_registers, 3, 100, 100, 100); 591e0a2b6dfSmrg break; 592e0a2b6dfSmrg case 340: 593e0a2b6dfSmrg default: 594894e0ac8Smrg set_color_register(color_registers, 0, 0, 0, 0); 595894e0ac8Smrg set_color_register(color_registers, 1, 20, 20, 80); 596894e0ac8Smrg set_color_register(color_registers, 2, 80, 13, 13); 597894e0ac8Smrg set_color_register(color_registers, 3, 20, 80, 20); 598894e0ac8Smrg set_color_register(color_registers, 4, 80, 20, 80); 599894e0ac8Smrg set_color_register(color_registers, 5, 20, 80, 80); 600894e0ac8Smrg set_color_register(color_registers, 6, 80, 80, 20); 601894e0ac8Smrg set_color_register(color_registers, 7, 53, 53, 53); 602894e0ac8Smrg set_color_register(color_registers, 8, 26, 26, 26); 603894e0ac8Smrg set_color_register(color_registers, 9, 33, 33, 60); 604894e0ac8Smrg set_color_register(color_registers, 10, 60, 26, 26); 605894e0ac8Smrg set_color_register(color_registers, 11, 33, 60, 33); 606894e0ac8Smrg set_color_register(color_registers, 12, 60, 33, 60); 607894e0ac8Smrg set_color_register(color_registers, 13, 33, 60, 60); 608894e0ac8Smrg set_color_register(color_registers, 14, 60, 60, 33); 609894e0ac8Smrg set_color_register(color_registers, 15, 80, 80, 80); 610894e0ac8Smrg break; 611894e0ac8Smrg case 382: /* FIXME: verify */ 612894e0ac8Smrg set_color_register(color_registers, 0, 0, 0, 0); 613894e0ac8Smrg set_color_register(color_registers, 1, 100, 100, 100); 614e0a2b6dfSmrg break; 615e0a2b6dfSmrg } 616e0a2b6dfSmrg 617894e0ac8Smrg#ifdef DEBUG_PALETTE 618894e0ac8Smrg { 619894e0ac8Smrg unsigned i; 620e0a2b6dfSmrg 621894e0ac8Smrg for (i = 0U; i < MAX_COLOR_REGISTERS; i++) { 62201037d57Smrg TRACE(("initial value for register %03u: %d,%d,%d\n", 623894e0ac8Smrg i, 624894e0ac8Smrg color_registers[i].r, 625894e0ac8Smrg color_registers[i].g, 62601037d57Smrg color_registers[i].b)); 627894e0ac8Smrg } 628894e0ac8Smrg } 629894e0ac8Smrg#endif 630894e0ac8Smrg} 631e0a2b6dfSmrg 632894e0ac8Smrgunsigned 633894e0ac8Smrgget_color_register_count(TScreen const *screen) 634894e0ac8Smrg{ 635894e0ac8Smrg unsigned num_color_registers; 636e0a2b6dfSmrg 637894e0ac8Smrg if (screen->numcolorregisters >= 0) { 638894e0ac8Smrg num_color_registers = (unsigned) screen->numcolorregisters; 639894e0ac8Smrg } else { 640894e0ac8Smrg num_color_registers = 0U; 641894e0ac8Smrg } 642e0a2b6dfSmrg 643894e0ac8Smrg if (num_color_registers > 1U) { 644894e0ac8Smrg if (num_color_registers > MAX_COLOR_REGISTERS) 645894e0ac8Smrg return MAX_COLOR_REGISTERS; 646894e0ac8Smrg return num_color_registers; 647894e0ac8Smrg } 648e0a2b6dfSmrg 649e0a2b6dfSmrg /* 650e0a2b6dfSmrg * color capabilities: 651e0a2b6dfSmrg * VK100/GIGI 1 plane (12x1 pixel attribute blocks) colorspace is 8 fixed colors (black, white, red, green, blue, cyan, yellow, magenta) 652e0a2b6dfSmrg * VT125 2 planes (4 registers) colorspace is (64?) (color), ? (grayscale) 653894e0ac8Smrg * VT240 2 planes (4 registers) colorspace is 4 shades (grayscale) 654e0a2b6dfSmrg * VT241 2 planes (4 registers) colorspace is ? (color), ? shades (grayscale) 655e0a2b6dfSmrg * VT330 2 planes (4 registers) colorspace is 4 shades (grayscale) 656e0a2b6dfSmrg * VT340 4 planes (16 registers) colorspace is r16g16b16 (color), 16 shades (grayscale) 657894e0ac8Smrg * VT382 1 plane (two fixed colors: black and white) FIXME: verify 658e0a2b6dfSmrg * dxterm ? 659e0a2b6dfSmrg */ 660894e0ac8Smrg switch (screen->terminal_id) { 661e0a2b6dfSmrg case 125: 662894e0ac8Smrg return 4U; 663e0a2b6dfSmrg case 240: 664894e0ac8Smrg return 4U; 665e0a2b6dfSmrg case 241: 666894e0ac8Smrg return 4U; 667e0a2b6dfSmrg case 330: 668894e0ac8Smrg return 4U; 669e0a2b6dfSmrg case 340: 670894e0ac8Smrg return 16U; 671894e0ac8Smrg case 382: 672894e0ac8Smrg return 2U; 673e0a2b6dfSmrg default: 674894e0ac8Smrg /* unknown graphics model -- might as well be generous */ 675894e0ac8Smrg return MAX_COLOR_REGISTERS; 676e0a2b6dfSmrg } 677894e0ac8Smrg} 678894e0ac8Smrg 679894e0ac8Smrgstatic void 680894e0ac8Smrginit_graphic(Graphic *graphic, 681894e0ac8Smrg unsigned type, 682894e0ac8Smrg int terminal_id, 683894e0ac8Smrg int charrow, 684894e0ac8Smrg int charcol, 685894e0ac8Smrg unsigned num_color_registers, 686894e0ac8Smrg int private_colors) 687894e0ac8Smrg{ 68801037d57Smrg const unsigned max_pixels = (unsigned) (graphic->max_width * 68901037d57Smrg graphic->max_height); 690894e0ac8Smrg unsigned i; 691894e0ac8Smrg 692894e0ac8Smrg TRACE(("initializing graphic object\n")); 693894e0ac8Smrg 6942e4f8982Smrg graphic->hidden = 0; 695894e0ac8Smrg graphic->dirty = 1; 69601037d57Smrg for (i = 0U; i < max_pixels; i++) 697894e0ac8Smrg graphic->pixels[i] = COLOR_HOLE; 698894e0ac8Smrg memset(graphic->color_registers_used, 0, sizeof(graphic->color_registers_used)); 699e0a2b6dfSmrg 700e0a2b6dfSmrg /* 701e0a2b6dfSmrg * text and graphics interactions: 702e0a2b6dfSmrg * VK100/GIGI text writes on top of graphics buffer, color attribute shared with text 703e0a2b6dfSmrg * VT240,VT241,VT330,VT340 text writes on top of graphics buffer 704894e0ac8Smrg * VT382 text writes on top of graphics buffer FIXME: verify 705e0a2b6dfSmrg * VT125 graphics buffer overlaid on top of text in B&W display, text not present in color display 706e0a2b6dfSmrg */ 707e0a2b6dfSmrg 708894e0ac8Smrg /* 709894e0ac8Smrg * dimensions (ReGIS logical, physical): 710894e0ac8Smrg * VK100/GIGI 768x4?? 768x240(status?) 711894e0ac8Smrg * VT125 768x460 768x230(+10status) (1:2 aspect ratio, ReGIS halves vertical addresses through "odd y emulation") 712894e0ac8Smrg * VT240 800x460 800x230(+10status) (1:2 aspect ratio, ReGIS halves vertical addresses through "odd y emulation") 713894e0ac8Smrg * VT241 800x460 800x230(+10status) (1:2 aspect ratio, ReGIS halves vertical addresses through "odd y emulation") 714894e0ac8Smrg * VT330 800x480 800x480(+?status) 715894e0ac8Smrg * VT340 800x480 800x480(+?status) 716894e0ac8Smrg * VT382 960x750 sixel only 717894e0ac8Smrg * dxterm ?x? ?x? variable? 718894e0ac8Smrg */ 719e0a2b6dfSmrg 720e0a2b6dfSmrg graphic->actual_width = 0; 721e0a2b6dfSmrg graphic->actual_height = 0; 722e0a2b6dfSmrg 723894e0ac8Smrg graphic->pixw = 1; 724894e0ac8Smrg graphic->pixh = 1; 725894e0ac8Smrg 726894e0ac8Smrg graphic->valid_registers = num_color_registers; 727894e0ac8Smrg TRACE(("%d color registers\n", graphic->valid_registers)); 728894e0ac8Smrg 729e0a2b6dfSmrg graphic->private_colors = private_colors; 730e0a2b6dfSmrg if (graphic->private_colors) { 731894e0ac8Smrg TRACE(("using private color registers\n")); 732e0a2b6dfSmrg init_color_registers(graphic->private_color_registers, terminal_id); 733e0a2b6dfSmrg graphic->color_registers = graphic->private_color_registers; 734e0a2b6dfSmrg } else { 735894e0ac8Smrg TRACE(("using shared color registers\n")); 736894e0ac8Smrg graphic->color_registers = getSharedRegisters(); 737e0a2b6dfSmrg } 738e0a2b6dfSmrg 739894e0ac8Smrg graphic->charrow = charrow; 740894e0ac8Smrg graphic->charcol = charcol; 741894e0ac8Smrg graphic->type = type; 742e0a2b6dfSmrg graphic->valid = 0; 743e0a2b6dfSmrg} 744e0a2b6dfSmrg 745894e0ac8SmrgGraphic * 746894e0ac8Smrgget_new_graphic(XtermWidget xw, int charrow, int charcol, unsigned type) 747e0a2b6dfSmrg{ 748e0a2b6dfSmrg TScreen const *screen = TScreenOf(xw); 749e0a2b6dfSmrg int bufferid = screen->whichBuf; 750e0a2b6dfSmrg int terminal_id = screen->terminal_id; 751894e0ac8Smrg Graphic *graphic; 752894e0ac8Smrg unsigned ii; 753e0a2b6dfSmrg 754894e0ac8Smrg FOR_EACH_SLOT(ii) { 75501037d57Smrg if ((graphic = getInactiveSlot(screen, ii))) { 756894e0ac8Smrg TRACE(("using fresh graphic index=%u id=%u\n", ii, next_graphic_id)); 757e0a2b6dfSmrg break; 758894e0ac8Smrg } 759e0a2b6dfSmrg } 760e0a2b6dfSmrg 761894e0ac8Smrg /* if none are free, recycle the graphic scrolled back the farthest */ 762894e0ac8Smrg if (!graphic) { 763e0a2b6dfSmrg int min_charrow = 0; 764894e0ac8Smrg Graphic *min_graphic = NULL; 765e0a2b6dfSmrg 766894e0ac8Smrg FOR_EACH_SLOT(ii) { 767894e0ac8Smrg if (!(graphic = getActiveSlot(ii))) 768894e0ac8Smrg continue; 769e0a2b6dfSmrg if (!min_graphic || graphic->charrow < min_charrow) { 770e0a2b6dfSmrg min_charrow = graphic->charrow; 771e0a2b6dfSmrg min_graphic = graphic; 772e0a2b6dfSmrg } 773e0a2b6dfSmrg } 774894e0ac8Smrg TRACE(("recycling old graphic index=%u id=%u\n", ii, next_graphic_id)); 775e0a2b6dfSmrg graphic = min_graphic; 776e0a2b6dfSmrg } 777e0a2b6dfSmrg 778894e0ac8Smrg if (graphic) { 779894e0ac8Smrg unsigned num_color_registers; 780894e0ac8Smrg num_color_registers = get_color_register_count(screen); 781894e0ac8Smrg graphic->xw = xw; 782894e0ac8Smrg graphic->bufferid = bufferid; 783894e0ac8Smrg graphic->id = next_graphic_id++; 784894e0ac8Smrg init_graphic(graphic, 785894e0ac8Smrg type, 786894e0ac8Smrg terminal_id, 787894e0ac8Smrg charrow, 788894e0ac8Smrg charcol, 789894e0ac8Smrg num_color_registers, 790894e0ac8Smrg screen->privatecolorregisters); 791894e0ac8Smrg } 792e0a2b6dfSmrg return graphic; 793e0a2b6dfSmrg} 794e0a2b6dfSmrg 795894e0ac8SmrgGraphic * 796894e0ac8Smrgget_new_or_matching_graphic(XtermWidget xw, 797894e0ac8Smrg int charrow, 798894e0ac8Smrg int charcol, 799894e0ac8Smrg int actual_width, 800894e0ac8Smrg int actual_height, 801894e0ac8Smrg unsigned type) 802e0a2b6dfSmrg{ 803894e0ac8Smrg TScreen const *screen = TScreenOf(xw); 804894e0ac8Smrg int bufferid = screen->whichBuf; 805894e0ac8Smrg Graphic *graphic; 806894e0ac8Smrg unsigned ii; 807894e0ac8Smrg 808894e0ac8Smrg FOR_EACH_SLOT(ii) { 8092e4f8982Smrg TRACE(("checking slot=%u for graphic at %d,%d %dx%d bufferid=%d type=%u\n", ii, 8102e4f8982Smrg charrow, charcol, 8112e4f8982Smrg actual_width, actual_height, 8122e4f8982Smrg bufferid, type)); 8132e4f8982Smrg if ((graphic = getActiveSlot(ii))) { 8142e4f8982Smrg if (graphic->type == type && 8152e4f8982Smrg graphic->bufferid == bufferid && 8162e4f8982Smrg graphic->charrow == charrow && 8172e4f8982Smrg graphic->charcol == charcol && 8182e4f8982Smrg graphic->actual_width == actual_width && 8192e4f8982Smrg graphic->actual_height == actual_height) { 8202e4f8982Smrg TRACE(("found existing graphic slot=%u id=%u\n", ii, graphic->id)); 8212e4f8982Smrg return graphic; 8222e4f8982Smrg } 8232e4f8982Smrg TRACE(("not a match: graphic at %d,%d %dx%d bufferid=%d type=%u\n", 8242e4f8982Smrg graphic->charrow, graphic->charcol, 8252e4f8982Smrg graphic->actual_width, graphic->actual_height, 8262e4f8982Smrg graphic->bufferid, graphic->type)); 827e0a2b6dfSmrg } 828e0a2b6dfSmrg } 829e0a2b6dfSmrg 830894e0ac8Smrg /* if no match get a new graphic */ 831894e0ac8Smrg if ((graphic = get_new_graphic(xw, charrow, charcol, type))) { 832894e0ac8Smrg graphic->actual_width = actual_width; 833894e0ac8Smrg graphic->actual_height = actual_height; 8342e4f8982Smrg TRACE(("no match; created graphic at %d,%d %dx%d bufferid=%d type=%u\n", 8352e4f8982Smrg graphic->charrow, graphic->charcol, 8362e4f8982Smrg graphic->actual_width, graphic->actual_height, 8372e4f8982Smrg graphic->bufferid, graphic->type)); 838e0a2b6dfSmrg } 839894e0ac8Smrg return graphic; 840e0a2b6dfSmrg} 841e0a2b6dfSmrg 84201037d57Smrgstatic int 84301037d57Smrglookup_allocated_color(const ColorRegister *reg, Pixel *pix) 844e0a2b6dfSmrg{ 84501037d57Smrg unsigned const rr = ((unsigned) reg->r * (LOOKUP_WIDTH - 1)) / CHANNEL_MAX; 84601037d57Smrg unsigned const gg = ((unsigned) reg->g * (LOOKUP_WIDTH - 1)) / CHANNEL_MAX; 84701037d57Smrg unsigned const bb = ((unsigned) reg->b * (LOOKUP_WIDTH - 1)) / CHANNEL_MAX; 84801037d57Smrg const AllocatedColorRegister *search; 84901037d57Smrg 85001037d57Smrg for (search = allocated_colors[rr][gg][bb]; search; search = search->next) { 85101037d57Smrg if (search->r == reg->r && 85201037d57Smrg search->g == reg->g && 85301037d57Smrg search->b == reg->b) { 85401037d57Smrg *pix = search->pix; 85501037d57Smrg return 1; 856e0a2b6dfSmrg } 857e0a2b6dfSmrg } 858e0a2b6dfSmrg 85901037d57Smrg *pix = 0UL; 86001037d57Smrg return 0; 86101037d57Smrg} 86201037d57Smrg 86301037d57Smrg#define ScaleForXColor(s) (unsigned short) ((long)(s) * 65535 / CHANNEL_MAX) 86401037d57Smrg 86501037d57Smrgstatic int 86601037d57Smrgsave_allocated_color(const ColorRegister *reg, XtermWidget xw, Pixel *pix) 86701037d57Smrg{ 86801037d57Smrg unsigned const rr = ((unsigned) reg->r * (LOOKUP_WIDTH - 1)) / CHANNEL_MAX; 86901037d57Smrg unsigned const gg = ((unsigned) reg->g * (LOOKUP_WIDTH - 1)) / CHANNEL_MAX; 87001037d57Smrg unsigned const bb = ((unsigned) reg->b * (LOOKUP_WIDTH - 1)) / CHANNEL_MAX; 87101037d57Smrg XColor xcolor; 87201037d57Smrg AllocatedColorRegister *new_color; 87301037d57Smrg 87401037d57Smrg xcolor.pixel = 0UL; 87501037d57Smrg xcolor.red = ScaleForXColor(reg->r); 87601037d57Smrg xcolor.green = ScaleForXColor(reg->g); 87701037d57Smrg xcolor.blue = ScaleForXColor(reg->b); 87801037d57Smrg xcolor.flags = DoRed | DoGreen | DoBlue; 87901037d57Smrg if (!allocateBestRGB(xw, &xcolor)) { 88001037d57Smrg TRACE(("unable to allocate xcolor\n")); 88101037d57Smrg *pix = 0UL; 88201037d57Smrg return 0; 88301037d57Smrg } 88401037d57Smrg 88501037d57Smrg *pix = xcolor.pixel; 88601037d57Smrg 88701037d57Smrg if (!(new_color = malloc(sizeof(*new_color)))) { 88801037d57Smrg TRACE(("unable to save pixel %lu\n", (unsigned long) *pix)); 88901037d57Smrg return 0; 89001037d57Smrg } 89101037d57Smrg new_color->r = reg->r; 89201037d57Smrg new_color->g = reg->g; 89301037d57Smrg new_color->b = reg->b; 89401037d57Smrg new_color->pix = *pix; 89501037d57Smrg new_color->next = allocated_colors[rr][gg][bb]; 89601037d57Smrg 89701037d57Smrg allocated_colors[rr][gg][bb] = new_color; 89801037d57Smrg 89901037d57Smrg return 1; 90001037d57Smrg} 90101037d57Smrg 90201037d57Smrgstatic Pixel 90301037d57Smrgcolor_register_to_xpixel(const ColorRegister *reg, XtermWidget xw) 90401037d57Smrg{ 90501037d57Smrg Pixel pix; 90601037d57Smrg 90701037d57Smrg if (!lookup_allocated_color(reg, &pix)) 90801037d57Smrg save_allocated_color(reg, xw, &pix); 90901037d57Smrg 910e0a2b6dfSmrg /* FIXME: with so many possible colors we need to determine 911e0a2b6dfSmrg * when to free them to be nice to PseudoColor displays 912e0a2b6dfSmrg */ 91301037d57Smrg return pix; 914e0a2b6dfSmrg} 915e0a2b6dfSmrg 916e0a2b6dfSmrgstatic void 917894e0ac8Smrgrefresh_graphic(TScreen const *screen, 918894e0ac8Smrg Graphic const *graphic, 91901037d57Smrg ColorRegister *buffer, 92001037d57Smrg int refresh_x, 92101037d57Smrg int refresh_y, 92201037d57Smrg int refresh_w, 92301037d57Smrg int refresh_h, 92401037d57Smrg int draw_x, 92501037d57Smrg int draw_y, 92601037d57Smrg int draw_w, 92701037d57Smrg int draw_h) 928e0a2b6dfSmrg{ 92901037d57Smrg int const pw = graphic->pixw; 93001037d57Smrg int const ph = graphic->pixh; 93101037d57Smrg int const graph_x = graphic->charcol * FontWidth(screen); 93201037d57Smrg int const graph_y = graphic->charrow * FontHeight(screen); 93301037d57Smrg int const graph_w = graphic->actual_width; 93401037d57Smrg int const graph_h = graphic->actual_height; 93501037d57Smrg int const mw = graphic->max_width; 936e0a2b6dfSmrg int r, c; 93701037d57Smrg int fillx, filly; 93801037d57Smrg int holes, total, out_of_range; 93901037d57Smrg RegisterNum regnum; 940e0a2b6dfSmrg 94101037d57Smrg TRACE(("refreshing graphic %u from %d,%d %dx%d (valid=%d, size=%dx%d, scale=%dx%d max=%dx%d)\n", 94201037d57Smrg graphic->id, 94301037d57Smrg graph_x, graph_y, draw_w, draw_h, 944e0a2b6dfSmrg graphic->valid, 945e0a2b6dfSmrg graphic->actual_width, 946e0a2b6dfSmrg graphic->actual_height, 94701037d57Smrg pw, ph, 948e0a2b6dfSmrg graphic->max_width, 94901037d57Smrg graphic->max_height)); 950e0a2b6dfSmrg 95101037d57Smrg TRACE(("refresh pixmap starts at %d,%d\n", refresh_x, refresh_y)); 952e0a2b6dfSmrg 953e0a2b6dfSmrg holes = total = 0; 95401037d57Smrg out_of_range = 0; 95501037d57Smrg for (r = 0; r < graph_h; r++) { 95601037d57Smrg int pmy = graph_y + r * ph; 957894e0ac8Smrg 95801037d57Smrg if (pmy + ph - 1 < draw_y) 959894e0ac8Smrg continue; 96001037d57Smrg if (pmy > draw_y + draw_h - 1) 96101037d57Smrg break; 962894e0ac8Smrg 96301037d57Smrg for (c = 0; c < graph_w; c++) { 96401037d57Smrg int pmx = graph_x + c * pw; 965e0a2b6dfSmrg 96601037d57Smrg if (pmx + pw - 1 < draw_x) 967894e0ac8Smrg continue; 96801037d57Smrg if (pmx > draw_x + draw_w - 1) 96901037d57Smrg break; 970e0a2b6dfSmrg 971e0a2b6dfSmrg total++; 97201037d57Smrg regnum = graphic->pixels[r * mw + c]; 97301037d57Smrg if (regnum == COLOR_HOLE) { 974e0a2b6dfSmrg holes++; 975e0a2b6dfSmrg continue; 976e0a2b6dfSmrg } 977e0a2b6dfSmrg 97801037d57Smrg for (fillx = 0; fillx < pw; fillx++) { 97901037d57Smrg for (filly = 0; filly < ph; filly++) { 98001037d57Smrg if (pmx < draw_x || pmx > draw_x + draw_w - 1 || 98101037d57Smrg pmy < draw_y || pmy > draw_y + draw_h - 1) { 98201037d57Smrg out_of_range++; 98301037d57Smrg continue; 98401037d57Smrg } 98501037d57Smrg 98601037d57Smrg /* this shouldn't happen, but it doesn't hurt to check */ 98701037d57Smrg if (pmx < refresh_x || pmx > refresh_x + refresh_w - 1 || 98801037d57Smrg pmy < refresh_y || pmy > refresh_y + refresh_h - 1) { 98901037d57Smrg TRACE(("OUT OF RANGE: %d,%d (%d,%d)\n", pmx, pmy, r, c)); 99001037d57Smrg out_of_range++; 99101037d57Smrg continue; 99201037d57Smrg } 99301037d57Smrg 99401037d57Smrg buffer[(pmy - refresh_y) * refresh_w + 99501037d57Smrg (pmx - refresh_x)] = 99601037d57Smrg graphic->color_registers[regnum]; 99701037d57Smrg } 998e0a2b6dfSmrg } 999e0a2b6dfSmrg } 1000894e0ac8Smrg } 1001e0a2b6dfSmrg 100201037d57Smrg TRACE(("done refreshing graphic: %d of %d refreshed pixels were holes; %d were out of pixmap range\n", 100301037d57Smrg holes, total, out_of_range)); 100401037d57Smrg} 100501037d57Smrg 1006e0a2b6dfSmrg#ifdef DEBUG_REFRESH 100701037d57Smrg 100801037d57Smrg#define BASEX(X) ( (draw_x - base_x) + (X) ) 100901037d57Smrg#define BASEY(Y) ( (draw_y - base_y) + (Y) ) 101001037d57Smrg 101101037d57Smrgstatic void 101201037d57Smrgoutline_refresh(TScreen const *screen, 101301037d57Smrg Graphic const *graphic, 101401037d57Smrg Pixmap output_pm, 101501037d57Smrg GC graphics_gc, 101601037d57Smrg int base_x, 101701037d57Smrg int base_y, 101801037d57Smrg int draw_x, 101901037d57Smrg int draw_y, 102001037d57Smrg int draw_w, 102101037d57Smrg int draw_h) 102201037d57Smrg{ 102301037d57Smrg Display *const display = screen->display; 102401037d57Smrg int const pw = graphic->pixw; 102501037d57Smrg int const ph = graphic->pixh; 102601037d57Smrg XGCValues xgcv; 102701037d57Smrg XColor def; 102801037d57Smrg 102901037d57Smrg def.red = (unsigned short) ((1.0 - 0.1 * (rand() / (double) 103001037d57Smrg RAND_MAX) * 65535.0)); 103101037d57Smrg def.green = (unsigned short) ((0.7 + 0.2 * (rand() / (double) 103201037d57Smrg RAND_MAX)) * 65535.0); 103301037d57Smrg def.blue = (unsigned short) ((0.1 + 0.1 * (rand() / (double) 103401037d57Smrg RAND_MAX)) * 65535.0); 103501037d57Smrg def.flags = DoRed | DoGreen | DoBlue; 103601037d57Smrg if (allocateBestRGB(graphic->xw, &def)) { 103701037d57Smrg xgcv.foreground = def.pixel; 103801037d57Smrg XChangeGC(display, graphics_gc, GCForeground, &xgcv); 1039e0a2b6dfSmrg } 1040e0a2b6dfSmrg 104101037d57Smrg XDrawLine(display, output_pm, graphics_gc, 104201037d57Smrg BASEX(0), BASEY(0), 104301037d57Smrg BASEX(draw_w - 1), BASEY(0)); 104401037d57Smrg XDrawLine(display, output_pm, graphics_gc, 104501037d57Smrg BASEX(0), BASEY(draw_h - 1), 104601037d57Smrg BASEX(draw_w - 1), BASEY(draw_h - 1)); 104701037d57Smrg 104801037d57Smrg XDrawLine(display, output_pm, graphics_gc, 104901037d57Smrg BASEX(0), BASEY(0), 105001037d57Smrg BASEX(0), BASEY(draw_h - 1)); 105101037d57Smrg XDrawLine(display, output_pm, graphics_gc, 105201037d57Smrg BASEX(draw_w - 1), BASEY(0), 105301037d57Smrg BASEX(draw_w - 1), BASEY(draw_h - 1)); 105401037d57Smrg 105501037d57Smrg XDrawLine(display, output_pm, graphics_gc, 105601037d57Smrg BASEX(draw_w - 1), BASEY(0), 105701037d57Smrg BASEX(0), BASEY(draw_h - 1)); 105801037d57Smrg XDrawLine(display, output_pm, graphics_gc, 105901037d57Smrg BASEX(draw_w - 1), BASEY(draw_h - 1), 106001037d57Smrg BASEX(0), BASEY(0)); 106101037d57Smrg 106201037d57Smrg def.red = (short) (0.7 * 65535.0); 106301037d57Smrg def.green = (short) (0.1 * 65535.0); 106401037d57Smrg def.blue = (short) (1.0 * 65535.0); 106501037d57Smrg def.flags = DoRed | DoGreen | DoBlue; 106601037d57Smrg if (allocateBestRGB(graphic->xw, &def)) { 106701037d57Smrg xgcv.foreground = def.pixel; 106801037d57Smrg XChangeGC(display, graphics_gc, GCForeground, &xgcv); 106901037d57Smrg } 107001037d57Smrg XFillRectangle(display, output_pm, graphics_gc, 107101037d57Smrg BASEX(0), 107201037d57Smrg BASEY(0), 107301037d57Smrg (unsigned) pw, (unsigned) ph); 107401037d57Smrg XFillRectangle(display, output_pm, graphics_gc, 107501037d57Smrg BASEX(draw_w - 1 - pw), 107601037d57Smrg BASEY(draw_h - 1 - ph), 107701037d57Smrg (unsigned) pw, (unsigned) ph); 1078e0a2b6dfSmrg} 107901037d57Smrg#endif 1080e0a2b6dfSmrg 1081e0a2b6dfSmrg/* 1082e0a2b6dfSmrg * Primary color hues: 1083e0a2b6dfSmrg * blue: 0 degrees 1084e0a2b6dfSmrg * red: 120 degrees 1085e0a2b6dfSmrg * green: 240 degrees 1086e0a2b6dfSmrg */ 1087894e0ac8Smrgvoid 1088e0a2b6dfSmrghls2rgb(int h, int l, int s, short *r, short *g, short *b) 1089e0a2b6dfSmrg{ 109001037d57Smrg const int hs = ((h + 240) / 60) % 6; 109101037d57Smrg const double lv = l / 100.0; 109201037d57Smrg const double sv = s / 100.0; 1093894e0ac8Smrg double c, x, m, c2; 1094e0a2b6dfSmrg double r1, g1, b1; 1095e0a2b6dfSmrg 1096e0a2b6dfSmrg if (s == 0) { 1097e0a2b6dfSmrg *r = *g = *b = (short) l; 1098e0a2b6dfSmrg return; 1099e0a2b6dfSmrg } 1100e0a2b6dfSmrg 110101037d57Smrg c2 = (2.0 * lv) - 1.0; 110201037d57Smrg if (c2 < 0.0) 1103894e0ac8Smrg c2 = -c2; 1104894e0ac8Smrg c = (1.0 - c2) * sv; 110501037d57Smrg x = (hs & 1) ? c : 0.0; 1106e0a2b6dfSmrg m = lv - 0.5 * c; 1107e0a2b6dfSmrg 110801037d57Smrg switch (hs) { 1109e0a2b6dfSmrg case 0: 1110e0a2b6dfSmrg r1 = c; 1111e0a2b6dfSmrg g1 = x; 1112e0a2b6dfSmrg b1 = 0.0; 1113e0a2b6dfSmrg break; 1114e0a2b6dfSmrg case 1: 1115e0a2b6dfSmrg r1 = x; 1116e0a2b6dfSmrg g1 = c; 1117e0a2b6dfSmrg b1 = 0.0; 1118e0a2b6dfSmrg break; 1119e0a2b6dfSmrg case 2: 1120e0a2b6dfSmrg r1 = 0.0; 1121e0a2b6dfSmrg g1 = c; 1122e0a2b6dfSmrg b1 = x; 1123e0a2b6dfSmrg break; 1124e0a2b6dfSmrg case 3: 1125e0a2b6dfSmrg r1 = 0.0; 1126e0a2b6dfSmrg g1 = x; 1127e0a2b6dfSmrg b1 = c; 1128e0a2b6dfSmrg break; 1129e0a2b6dfSmrg case 4: 1130e0a2b6dfSmrg r1 = x; 1131e0a2b6dfSmrg g1 = 0.0; 1132e0a2b6dfSmrg b1 = c; 1133e0a2b6dfSmrg break; 1134e0a2b6dfSmrg case 5: 1135e0a2b6dfSmrg r1 = c; 1136e0a2b6dfSmrg g1 = 0.0; 1137e0a2b6dfSmrg b1 = x; 1138e0a2b6dfSmrg break; 1139e0a2b6dfSmrg default: 1140894e0ac8Smrg TRACE(("Bad HLS input: [%d,%d,%d], returning white\n", h, l, s)); 1141e0a2b6dfSmrg *r = (short) 100; 1142e0a2b6dfSmrg *g = (short) 100; 1143e0a2b6dfSmrg *b = (short) 100; 1144e0a2b6dfSmrg return; 1145e0a2b6dfSmrg } 1146e0a2b6dfSmrg 1147e0a2b6dfSmrg *r = (short) ((r1 + m) * 100.0 + 0.5); 1148e0a2b6dfSmrg *g = (short) ((g1 + m) * 100.0 + 0.5); 1149e0a2b6dfSmrg *b = (short) ((b1 + m) * 100.0 + 0.5); 1150e0a2b6dfSmrg 1151e0a2b6dfSmrg if (*r < 0) 1152e0a2b6dfSmrg *r = 0; 1153e0a2b6dfSmrg else if (*r > 100) 1154e0a2b6dfSmrg *r = 100; 1155e0a2b6dfSmrg if (*g < 0) 1156e0a2b6dfSmrg *g = 0; 1157e0a2b6dfSmrg else if (*g > 100) 1158e0a2b6dfSmrg *g = 100; 1159e0a2b6dfSmrg if (*b < 0) 1160e0a2b6dfSmrg *b = 0; 1161e0a2b6dfSmrg else if (*b > 100) 1162e0a2b6dfSmrg *b = 100; 1163e0a2b6dfSmrg} 1164e0a2b6dfSmrg 1165894e0ac8Smrgvoid 1166894e0ac8Smrgdump_graphic(Graphic const *graphic) 1167e0a2b6dfSmrg{ 1168894e0ac8Smrg#if defined(DUMP_COLORS) || defined(DUMP_BITMAP) 1169894e0ac8Smrg RegisterNum color; 1170894e0ac8Smrg#endif 1171894e0ac8Smrg#ifdef DUMP_BITMAP 1172894e0ac8Smrg int r, c; 1173894e0ac8Smrg ColorRegister const *reg; 1174894e0ac8Smrg#endif 1175e0a2b6dfSmrg 1176894e0ac8Smrg (void) graphic; 1177e0a2b6dfSmrg 1178894e0ac8Smrg TRACE(("graphic stats: id=%u charrow=%d charcol=%d actual_width=%d actual_height=%d pixw=%d pixh=%d\n", 1179894e0ac8Smrg graphic->id, 1180894e0ac8Smrg graphic->charrow, 1181894e0ac8Smrg graphic->charcol, 1182894e0ac8Smrg graphic->actual_width, 1183894e0ac8Smrg graphic->actual_height, 1184894e0ac8Smrg graphic->pixw, 1185894e0ac8Smrg graphic->pixh)); 1186e0a2b6dfSmrg 1187894e0ac8Smrg#ifdef DUMP_COLORS 1188894e0ac8Smrg TRACE(("graphic colors:\n")); 1189894e0ac8Smrg for (color = 0; color < graphic->valid_registers; color++) { 1190894e0ac8Smrg TRACE(("%03u: %d,%d,%d\n", 1191894e0ac8Smrg color, 1192894e0ac8Smrg graphic->color_registers[color].r, 1193894e0ac8Smrg graphic->color_registers[color].g, 1194894e0ac8Smrg graphic->color_registers[color].b)); 1195e0a2b6dfSmrg } 1196e0a2b6dfSmrg#endif 1197e0a2b6dfSmrg 1198894e0ac8Smrg#ifdef DUMP_BITMAP 1199894e0ac8Smrg TRACE(("graphic pixels:\n")); 1200894e0ac8Smrg for (r = 0; r < graphic->actual_height; r++) { 1201894e0ac8Smrg for (c = 0; c < graphic->actual_width; c++) { 1202894e0ac8Smrg color = graphic->pixels[r * graphic->max_width + c]; 1203894e0ac8Smrg if (color == COLOR_HOLE) { 1204894e0ac8Smrg TRACE(("?")); 1205e0a2b6dfSmrg } else { 1206894e0ac8Smrg reg = &graphic->color_registers[color]; 1207894e0ac8Smrg if (reg->r + reg->g + reg->b > 200) { 1208894e0ac8Smrg TRACE(("#")); 1209894e0ac8Smrg } else if (reg->r + reg->g + reg->b > 150) { 1210894e0ac8Smrg TRACE(("%%")); 1211894e0ac8Smrg } else if (reg->r + reg->g + reg->b > 100) { 1212894e0ac8Smrg TRACE((":")); 1213894e0ac8Smrg } else if (reg->r + reg->g + reg->b > 80) { 1214894e0ac8Smrg TRACE((".")); 1215894e0ac8Smrg } else { 1216894e0ac8Smrg TRACE((" ")); 1217e0a2b6dfSmrg } 1218e0a2b6dfSmrg } 1219e0a2b6dfSmrg } 1220894e0ac8Smrg TRACE(("\n")); 1221e0a2b6dfSmrg } 1222e0a2b6dfSmrg 1223894e0ac8Smrg TRACE(("\n")); 1224894e0ac8Smrg#endif 1225e0a2b6dfSmrg} 1226e0a2b6dfSmrg 1227e0a2b6dfSmrg/* Erase the portion of any displayed graphic overlapping with a rectangle 122801037d57Smrg * of the given size and location in pixels relative to the start of the 122901037d57Smrg * graphic. This is used to allow text to "erase" graphics underneath it. 1230e0a2b6dfSmrg */ 1231e0a2b6dfSmrgstatic void 1232894e0ac8Smrgerase_graphic(Graphic *graphic, int x, int y, int w, int h) 1233e0a2b6dfSmrg{ 1234e0a2b6dfSmrg RegisterNum hole = COLOR_HOLE; 1235e0a2b6dfSmrg int pw, ph; 1236e0a2b6dfSmrg int r, c; 1237894e0ac8Smrg int rbase, cbase; 1238e0a2b6dfSmrg 1239e0a2b6dfSmrg pw = graphic->pixw; 1240e0a2b6dfSmrg ph = graphic->pixh; 1241e0a2b6dfSmrg 1242894e0ac8Smrg TRACE(("erasing graphic %d,%d %dx%d\n", x, y, w, h)); 1243e0a2b6dfSmrg 1244894e0ac8Smrg rbase = 0; 1245e0a2b6dfSmrg for (r = 0; r < graphic->actual_height; r++) { 1246894e0ac8Smrg if (rbase + ph - 1 >= y 1247894e0ac8Smrg && rbase <= y + h - 1) { 1248894e0ac8Smrg cbase = 0; 1249894e0ac8Smrg for (c = 0; c < graphic->actual_width; c++) { 1250894e0ac8Smrg if (cbase + pw - 1 >= x 1251894e0ac8Smrg && cbase <= x + w - 1) { 1252894e0ac8Smrg graphic->pixels[r * graphic->max_width + c] = hole; 1253894e0ac8Smrg } 1254894e0ac8Smrg cbase += pw; 1255894e0ac8Smrg } 1256e0a2b6dfSmrg } 1257894e0ac8Smrg rbase += ph; 1258e0a2b6dfSmrg } 1259e0a2b6dfSmrg} 1260e0a2b6dfSmrg 1261e0a2b6dfSmrgstatic int 1262894e0ac8Smrgcompare_graphic_ids(const void *left, const void *right) 1263e0a2b6dfSmrg{ 1264894e0ac8Smrg const Graphic *l = *(const Graphic *const *) left; 1265894e0ac8Smrg const Graphic *r = *(const Graphic *const *) right; 1266e0a2b6dfSmrg 1267e0a2b6dfSmrg if (!l->valid || !r->valid) 1268e0a2b6dfSmrg return 0; 126901037d57Smrg 127001037d57Smrg if (l->bufferid < r->bufferid) 127101037d57Smrg return -1; 127201037d57Smrg else if (l->bufferid > r->bufferid) 127301037d57Smrg return 1; 127401037d57Smrg 1275e0a2b6dfSmrg if (l->id < r->id) 1276e0a2b6dfSmrg return -1; 1277e0a2b6dfSmrg else 1278e0a2b6dfSmrg return 1; 1279e0a2b6dfSmrg} 1280e0a2b6dfSmrg 128101037d57Smrgstatic void 128201037d57Smrgclip_area(int *orig_x, int *orig_y, int *orig_w, int *orig_h, 128301037d57Smrg int clip_x, int clip_y, int clip_w, int clip_h) 1284e0a2b6dfSmrg{ 128501037d57Smrg if (*orig_x < clip_x) { 128601037d57Smrg const int diff = clip_x - *orig_x; 128701037d57Smrg *orig_x += diff; 128801037d57Smrg *orig_w -= diff; 128901037d57Smrg } 129001037d57Smrg if (*orig_w > 0 && *orig_x + *orig_w > clip_x + clip_w) { 129101037d57Smrg *orig_w -= (*orig_x + *orig_w) - (clip_x + clip_w); 129201037d57Smrg } 129301037d57Smrg 129401037d57Smrg if (*orig_y < clip_y) { 129501037d57Smrg const int diff = clip_y - *orig_y; 129601037d57Smrg *orig_y += diff; 129701037d57Smrg *orig_h -= diff; 129801037d57Smrg } 129901037d57Smrg if (*orig_h > 0 && *orig_y + *orig_h > clip_y + clip_h) { 130001037d57Smrg *orig_h -= (*orig_y + *orig_h) - (clip_y + clip_h); 130101037d57Smrg } 130201037d57Smrg} 130301037d57Smrg 130401037d57Smrg/* the coordinates are relative to the screen */ 130501037d57Smrgstatic void 130601037d57Smrgrefresh_graphics(XtermWidget xw, 130701037d57Smrg int leftcol, 130801037d57Smrg int toprow, 130901037d57Smrg int ncols, 131001037d57Smrg int nrows, 131101037d57Smrg int skip_clean) 131201037d57Smrg{ 131301037d57Smrg TScreen *const screen = TScreenOf(xw); 131401037d57Smrg Display *const display = screen->display; 131501037d57Smrg Window const drawable = VDrawable(screen); 131601037d57Smrg int const scroll_y = screen->topline * FontHeight(screen); 131701037d57Smrg int const refresh_x = leftcol * FontWidth(screen); 131801037d57Smrg int const refresh_y = toprow * FontHeight(screen) + scroll_y; 131901037d57Smrg int const refresh_w = ncols * FontWidth(screen); 132001037d57Smrg int const refresh_h = nrows * FontHeight(screen); 132101037d57Smrg int draw_x_min, draw_x_max; 132201037d57Smrg int draw_y_min, draw_y_max; 1323894e0ac8Smrg Graphic *ordered_graphics[MAX_GRAPHICS]; 132401037d57Smrg unsigned ii, jj; 132501037d57Smrg unsigned active_count; 132601037d57Smrg unsigned holes, non_holes; 132701037d57Smrg int xx, yy; 132801037d57Smrg ColorRegister *buffer; 1329e0a2b6dfSmrg 133001037d57Smrg active_count = 0; 1331894e0ac8Smrg FOR_EACH_SLOT(ii) { 133201037d57Smrg Graphic *graphic; 133301037d57Smrg if (!(graphic = getActiveSlot(ii))) 133401037d57Smrg continue; 133501037d57Smrg TRACE(("refreshing graphic %d on buffer %d, current buffer %d\n", 133601037d57Smrg graphic->id, graphic->bufferid, screen->whichBuf)); 133701037d57Smrg if (screen->whichBuf == 0) { 133801037d57Smrg if (graphic->bufferid != 0) { 133901037d57Smrg TRACE(("skipping graphic %d from alt buffer (%d) when drawing screen=%d\n", 134001037d57Smrg graphic->id, graphic->bufferid, screen->whichBuf)); 134101037d57Smrg continue; 134201037d57Smrg } 134301037d57Smrg } else { 134401037d57Smrg if (graphic->bufferid == 0 && graphic->charrow >= 0) { 134501037d57Smrg TRACE(("skipping graphic %d from normal buffer (%d) when drawing screen=%d because it is not in scrollback area\n", 134601037d57Smrg graphic->id, graphic->bufferid, screen->whichBuf)); 134701037d57Smrg continue; 134801037d57Smrg } 134901037d57Smrg if (graphic->bufferid == 1 && 135001037d57Smrg graphic->charrow + (graphic->actual_height + 135101037d57Smrg FontHeight(screen) - 1) / 135201037d57Smrg FontHeight(screen) < 0) { 135301037d57Smrg TRACE(("skipping graphic %d from alt buffer (%d) when drawing screen=%d because it is completely in scrollback area\n", 135401037d57Smrg graphic->id, graphic->bufferid, screen->whichBuf)); 135501037d57Smrg continue; 135601037d57Smrg } 1357894e0ac8Smrg } 13582e4f8982Smrg if (graphic->hidden) 13592e4f8982Smrg continue; 136001037d57Smrg ordered_graphics[active_count++] = graphic; 1361894e0ac8Smrg } 136201037d57Smrg 136301037d57Smrg if (active_count == 0) 136401037d57Smrg return; 136501037d57Smrg if (active_count > 1) { 1366894e0ac8Smrg qsort(ordered_graphics, 136701037d57Smrg (size_t) active_count, 1368894e0ac8Smrg sizeof(ordered_graphics[0]), 1369894e0ac8Smrg compare_graphic_ids); 1370e0a2b6dfSmrg } 1371e0a2b6dfSmrg 137201037d57Smrg if (skip_clean) { 137301037d57Smrg unsigned skip_count; 1374e0a2b6dfSmrg 137501037d57Smrg for (jj = 0; jj < active_count; ++jj) { 137601037d57Smrg if (ordered_graphics[jj]->dirty) 137701037d57Smrg break; 1378e0a2b6dfSmrg } 137901037d57Smrg skip_count = jj; 138001037d57Smrg if (skip_count == active_count) 138101037d57Smrg return; 1382e0a2b6dfSmrg 138301037d57Smrg active_count -= skip_count; 138401037d57Smrg for (jj = 0; jj < active_count; ++jj) { 138501037d57Smrg ordered_graphics[jj] = ordered_graphics[jj + skip_count]; 138601037d57Smrg } 1387e0a2b6dfSmrg } 1388e0a2b6dfSmrg 138901037d57Smrg if (!(buffer = malloc(sizeof(*buffer) * 139001037d57Smrg (unsigned) refresh_w * (unsigned) refresh_h))) { 139101037d57Smrg TRACE(("unable to allocate %dx%d buffer for graphics refresh\n", 139201037d57Smrg refresh_w, refresh_h)); 139301037d57Smrg return; 139401037d57Smrg } 139501037d57Smrg for (yy = 0; yy < refresh_h; yy++) { 139601037d57Smrg for (xx = 0; xx < refresh_w; xx++) { 139701037d57Smrg buffer[yy * refresh_w + xx].r = -1; 139801037d57Smrg buffer[yy * refresh_w + xx].g = -1; 139901037d57Smrg buffer[yy * refresh_w + xx].b = -1; 140001037d57Smrg } 140101037d57Smrg } 1402e0a2b6dfSmrg 140301037d57Smrg TRACE(("refresh: screen->topline=%d leftcol=%d toprow=%d nrows=%d ncols=%d (%d,%d %dx%d)\n", 140401037d57Smrg screen->topline, 140501037d57Smrg leftcol, toprow, 140601037d57Smrg nrows, ncols, 140701037d57Smrg refresh_x, refresh_y, 140801037d57Smrg refresh_w, refresh_h)); 1409e0a2b6dfSmrg 141001037d57Smrg { 141101037d57Smrg int const altarea_x = 0; 141201037d57Smrg int const altarea_y = 0; 141301037d57Smrg int const altarea_w = Width(screen) * FontWidth(screen); 141401037d57Smrg int const altarea_h = Height(screen) * FontHeight(screen); 141501037d57Smrg 141601037d57Smrg int const scrollarea_x = 0; 141701037d57Smrg int const scrollarea_y = scroll_y; 141801037d57Smrg int const scrollarea_w = Width(screen) * FontWidth(screen); 141901037d57Smrg int const scrollarea_h = -scroll_y; 142001037d57Smrg 142101037d57Smrg int const mainarea_x = 0; 142201037d57Smrg int const mainarea_y = scroll_y; 142301037d57Smrg int const mainarea_w = Width(screen) * FontWidth(screen); 142401037d57Smrg int const mainarea_h = -scroll_y + Height(screen) * FontHeight(screen); 142501037d57Smrg 142601037d57Smrg draw_x_min = refresh_x + refresh_w; 142701037d57Smrg draw_x_max = refresh_x - 1; 142801037d57Smrg draw_y_min = refresh_y + refresh_h; 142901037d57Smrg draw_y_max = refresh_y - 1; 143001037d57Smrg for (jj = 0; jj < active_count; ++jj) { 143101037d57Smrg Graphic *graphic = ordered_graphics[jj]; 143201037d57Smrg int draw_x = graphic->charcol * FontWidth(screen); 143301037d57Smrg int draw_y = graphic->charrow * FontHeight(screen); 143401037d57Smrg int draw_w = graphic->actual_width; 143501037d57Smrg int draw_h = graphic->actual_height; 143601037d57Smrg 143701037d57Smrg if (screen->whichBuf != 0) { 143801037d57Smrg if (graphic->bufferid != 0) { 143901037d57Smrg /* clip to alt buffer */ 144001037d57Smrg clip_area(&draw_x, &draw_y, &draw_w, &draw_h, 144101037d57Smrg altarea_x, altarea_y, altarea_w, altarea_h); 14422e4f8982Smrg } else { 144301037d57Smrg /* clip to scrollback area */ 144401037d57Smrg clip_area(&draw_x, &draw_y, &draw_w, &draw_h, 144501037d57Smrg scrollarea_x, scrollarea_y, 144601037d57Smrg scrollarea_w, scrollarea_h); 144701037d57Smrg } 144801037d57Smrg } else { 144901037d57Smrg /* clip to scrollback + normal area */ 145001037d57Smrg clip_area(&draw_x, &draw_y, &draw_w, &draw_h, 145101037d57Smrg mainarea_x, mainarea_y, 145201037d57Smrg mainarea_w, mainarea_h); 145301037d57Smrg } 145401037d57Smrg 145501037d57Smrg clip_area(&draw_x, &draw_y, &draw_w, &draw_h, 145601037d57Smrg refresh_x, refresh_y, refresh_w, refresh_h); 145701037d57Smrg 145801037d57Smrg TRACE(("refresh: graph=%u\n", jj)); 145901037d57Smrg TRACE((" refresh_x=%d refresh_y=%d refresh_w=%d refresh_h=%d\n", 146001037d57Smrg refresh_x, refresh_y, refresh_w, refresh_h)); 146101037d57Smrg TRACE((" draw_x=%d draw_y=%d draw_w=%d draw_h=%d\n", 146201037d57Smrg draw_x, draw_y, draw_w, draw_h)); 146301037d57Smrg 146401037d57Smrg if (draw_w > 0 && draw_h > 0) { 146501037d57Smrg refresh_graphic(screen, graphic, buffer, 146601037d57Smrg refresh_x, refresh_y, 146701037d57Smrg refresh_w, refresh_h, 146801037d57Smrg draw_x, draw_y, 146901037d57Smrg draw_w, draw_h); 147001037d57Smrg if (draw_x < draw_x_min) 147101037d57Smrg draw_x_min = draw_x; 147201037d57Smrg if (draw_x + draw_w - 1 > draw_x_max) 147301037d57Smrg draw_x_max = draw_x + draw_w - 1; 147401037d57Smrg if (draw_y < draw_y_min) 147501037d57Smrg draw_y_min = draw_y; 147601037d57Smrg if (draw_y + draw_h - 1 > draw_y_max) 147701037d57Smrg draw_y_max = draw_y + draw_h - 1; 147801037d57Smrg } 147901037d57Smrg graphic->dirty = 0; 1480e0a2b6dfSmrg } 148101037d57Smrg } 1482e0a2b6dfSmrg 148301037d57Smrg if (draw_x_max < refresh_x || 148401037d57Smrg draw_x_min > refresh_x + refresh_w - 1 || 148501037d57Smrg draw_y_max < refresh_y || 148601037d57Smrg draw_y_min > refresh_y + refresh_h - 1) { 148701037d57Smrg free(buffer); 148801037d57Smrg return; 1489e0a2b6dfSmrg } 149001037d57Smrg 149101037d57Smrg holes = 0U; 149201037d57Smrg non_holes = 0U; 149301037d57Smrg for (yy = draw_y_min - refresh_y; yy <= draw_y_max - refresh_y; yy++) { 149401037d57Smrg for (xx = draw_x_min - refresh_x; xx <= draw_x_max - refresh_x; xx++) { 149501037d57Smrg const ColorRegister color = buffer[yy * refresh_w + xx]; 149601037d57Smrg if (color.r < 0 || color.g < 0 || color.b < 0) { 149701037d57Smrg holes++; 149801037d57Smrg } else { 149901037d57Smrg non_holes++; 150001037d57Smrg } 150101037d57Smrg } 150201037d57Smrg } 150301037d57Smrg 150401037d57Smrg if (non_holes < 1U) { 150501037d57Smrg TRACE(("refresh: visible graphics areas are erased; nothing to do\n")); 150601037d57Smrg free(buffer); 150701037d57Smrg return; 150801037d57Smrg } 150901037d57Smrg 151001037d57Smrg /* 151101037d57Smrg * If we have any holes we can't just copy an image rectangle, and masking 151201037d57Smrg * with bitmaps is very expensive. This fallback is surprisingly faster 151301037d57Smrg * than the XPutImage version in some cases, but I don't know why. 151401037d57Smrg * (This is even though there's no X11 primitive for drawing a horizontal 151501037d57Smrg * line of height one and no attempt is made to handle multiple lines at 151601037d57Smrg * once.) 151701037d57Smrg */ 151801037d57Smrg if (holes > 0U) { 151901037d57Smrg GC graphics_gc; 152001037d57Smrg XGCValues xgcv; 152101037d57Smrg ColorRegister last_color; 152201037d57Smrg ColorRegister gc_color; 152301037d57Smrg int run; 152401037d57Smrg 152501037d57Smrg memset(&xgcv, 0, sizeof(xgcv)); 152601037d57Smrg xgcv.graphics_exposures = False; 152701037d57Smrg graphics_gc = XCreateGC(display, drawable, GCGraphicsExposures, &xgcv); 152801037d57Smrg if (graphics_gc == None) { 152901037d57Smrg TRACE(("unable to allocate GC for graphics refresh\n")); 153001037d57Smrg free(buffer); 153101037d57Smrg return; 153201037d57Smrg } 153301037d57Smrg 153401037d57Smrg last_color.r = -1; 153501037d57Smrg last_color.g = -1; 153601037d57Smrg last_color.b = -1; 153701037d57Smrg gc_color.r = -1; 153801037d57Smrg gc_color.g = -1; 153901037d57Smrg gc_color.b = -1; 154001037d57Smrg run = 0; 154101037d57Smrg for (yy = draw_y_min - refresh_y; yy <= draw_y_max - refresh_y; yy++) { 154201037d57Smrg for (xx = draw_x_min - refresh_x; xx <= draw_x_max - refresh_x; 154301037d57Smrg xx++) { 154401037d57Smrg const ColorRegister color = buffer[yy * refresh_w + xx]; 154501037d57Smrg 154601037d57Smrg if (color.r < 0 || color.g < 0 || color.b < 0) { 154701037d57Smrg last_color = color; 154801037d57Smrg if (run > 0) { 154901037d57Smrg XDrawLine(display, drawable, graphics_gc, 155001037d57Smrg OriginX(screen) + refresh_x + xx - run, 155101037d57Smrg (OriginY(screen) - scroll_y) + refresh_y + yy, 155201037d57Smrg OriginX(screen) + refresh_x + xx - 1, 155301037d57Smrg (OriginY(screen) - scroll_y) + refresh_y + yy); 155401037d57Smrg run = 0; 155501037d57Smrg } 155601037d57Smrg continue; 155701037d57Smrg } 155801037d57Smrg 155901037d57Smrg if (color.r != last_color.r || 156001037d57Smrg color.g != last_color.g || 156101037d57Smrg color.b != last_color.b) { 156201037d57Smrg last_color = color; 156301037d57Smrg if (run > 0) { 156401037d57Smrg XDrawLine(display, drawable, graphics_gc, 156501037d57Smrg OriginX(screen) + refresh_x + xx - run, 156601037d57Smrg (OriginY(screen) - scroll_y) + refresh_y + yy, 156701037d57Smrg OriginX(screen) + refresh_x + xx - 1, 156801037d57Smrg (OriginY(screen) - scroll_y) + refresh_y + yy); 156901037d57Smrg run = 0; 157001037d57Smrg } 157101037d57Smrg 157201037d57Smrg if (color.r != gc_color.r || 157301037d57Smrg color.g != gc_color.g || 157401037d57Smrg color.b != gc_color.b) { 157501037d57Smrg xgcv.foreground = 157601037d57Smrg color_register_to_xpixel(&color, xw); 157701037d57Smrg XChangeGC(display, graphics_gc, GCForeground, &xgcv); 157801037d57Smrg gc_color = color; 157901037d57Smrg } 158001037d57Smrg } 158101037d57Smrg run++; 158201037d57Smrg } 158301037d57Smrg if (run > 0) { 158401037d57Smrg last_color.r = -1; 158501037d57Smrg last_color.g = -1; 158601037d57Smrg last_color.b = -1; 158701037d57Smrg XDrawLine(display, drawable, graphics_gc, 158801037d57Smrg OriginX(screen) + refresh_x + xx - run, 158901037d57Smrg (OriginY(screen) - scroll_y) + refresh_y + yy, 159001037d57Smrg OriginX(screen) + refresh_x + xx - 1, 159101037d57Smrg (OriginY(screen) - scroll_y) + refresh_y + yy); 159201037d57Smrg run = 0; 159301037d57Smrg } 159401037d57Smrg } 159501037d57Smrg 159601037d57Smrg XFreeGC(display, graphics_gc); 159701037d57Smrg } else { 159801037d57Smrg XGCValues xgcv; 159901037d57Smrg GC graphics_gc; 160001037d57Smrg ColorRegister old_color; 160101037d57Smrg Pixel fg; 160201037d57Smrg XImage *image; 160301037d57Smrg char *imgdata; 160401037d57Smrg unsigned image_w, image_h; 160501037d57Smrg 160601037d57Smrg memset(&xgcv, 0, sizeof(xgcv)); 160701037d57Smrg xgcv.graphics_exposures = False; 160801037d57Smrg graphics_gc = XCreateGC(display, drawable, GCGraphicsExposures, &xgcv); 160901037d57Smrg if (graphics_gc == None) { 161001037d57Smrg TRACE(("unable to allocate GC for graphics refresh\n")); 161101037d57Smrg free(buffer); 161201037d57Smrg return; 161301037d57Smrg } 161401037d57Smrg 161501037d57Smrg /* FIXME: is it worth reusing the GC/Image/imagedata across calls? */ 161601037d57Smrg /* FIXME: is it worth using shared memory when available? */ 161701037d57Smrg image_w = (unsigned) draw_x_max + 1U - (unsigned) draw_x_min; 161801037d57Smrg image_h = (unsigned) draw_y_max + 1U - (unsigned) draw_y_min; 161901037d57Smrg image = XCreateImage(display, xw->visInfo->visual, 162001037d57Smrg (unsigned) xw->visInfo->depth, 162101037d57Smrg ZPixmap, 0, NULL, 162201037d57Smrg image_w, image_h, 162301037d57Smrg sizeof(int) * 8U, 0); 162401037d57Smrg if (!image) { 162501037d57Smrg TRACE(("unable to allocate XImage for graphics refresh\n")); 162601037d57Smrg XFreeGC(display, graphics_gc); 162701037d57Smrg free(buffer); 162801037d57Smrg return; 162901037d57Smrg } 163001037d57Smrg imgdata = malloc(image_h * (unsigned) image->bytes_per_line); 163101037d57Smrg if (!imgdata) { 163201037d57Smrg TRACE(("unable to allocate XImage for graphics refresh\n")); 163301037d57Smrg XDestroyImage(image); 163401037d57Smrg XFreeGC(display, graphics_gc); 163501037d57Smrg free(buffer); 163601037d57Smrg return; 163701037d57Smrg } 163801037d57Smrg image->data = imgdata; 163901037d57Smrg 164001037d57Smrg fg = 0U; 164101037d57Smrg old_color.r = -1; 164201037d57Smrg old_color.g = -1; 164301037d57Smrg old_color.b = -1; 164401037d57Smrg for (yy = draw_y_min - refresh_y; yy <= draw_y_max - refresh_y; yy++) { 164501037d57Smrg for (xx = draw_x_min - refresh_x; xx <= draw_x_max - refresh_x; 164601037d57Smrg xx++) { 164701037d57Smrg const ColorRegister color = buffer[yy * refresh_w + xx]; 164801037d57Smrg 164901037d57Smrg if (color.r != old_color.r || 165001037d57Smrg color.g != old_color.g || 165101037d57Smrg color.b != old_color.b) { 165201037d57Smrg fg = color_register_to_xpixel(&color, xw); 165301037d57Smrg old_color = color; 165401037d57Smrg } 165501037d57Smrg 165601037d57Smrg XPutPixel(image, xx + refresh_x - draw_x_min, 165701037d57Smrg yy + refresh_y - draw_y_min, fg); 165801037d57Smrg } 165901037d57Smrg } 166001037d57Smrg 166101037d57Smrg XPutImage(display, drawable, graphics_gc, image, 166201037d57Smrg 0, 0, 166301037d57Smrg OriginX(screen) + draw_x_min, 166401037d57Smrg (OriginY(screen) - scroll_y) + draw_y_min, 166501037d57Smrg image_w, image_h); 166601037d57Smrg free(imgdata); 166701037d57Smrg image->data = NULL; 166801037d57Smrg XDestroyImage(image); 166901037d57Smrg XFreeGC(display, graphics_gc); 167001037d57Smrg } 167101037d57Smrg 167201037d57Smrg free(buffer); 167301037d57Smrg XFlush(display); 167401037d57Smrg} 167501037d57Smrg 167601037d57Smrgvoid 167701037d57Smrgrefresh_displayed_graphics(XtermWidget xw, 167801037d57Smrg int leftcol, 167901037d57Smrg int toprow, 168001037d57Smrg int ncols, 168101037d57Smrg int nrows) 168201037d57Smrg{ 168301037d57Smrg refresh_graphics(xw, leftcol, toprow, ncols, nrows, 0); 168401037d57Smrg} 168501037d57Smrg 168601037d57Smrgvoid 168701037d57Smrgrefresh_modified_displayed_graphics(XtermWidget xw) 168801037d57Smrg{ 168901037d57Smrg TScreen const *screen = TScreenOf(xw); 169001037d57Smrg refresh_graphics(xw, 0, 0, MaxCols(screen), MaxRows(screen), 1); 1691e0a2b6dfSmrg} 1692e0a2b6dfSmrg 1693894e0ac8Smrgvoid 169401037d57Smrgscroll_displayed_graphics(XtermWidget xw, int rows) 1695e0a2b6dfSmrg{ 169601037d57Smrg TScreen const *screen = TScreenOf(xw); 1697894e0ac8Smrg unsigned ii; 1698e0a2b6dfSmrg 1699e0a2b6dfSmrg TRACE(("graphics scroll: moving all up %d rows\n", rows)); 1700894e0ac8Smrg /* FIXME: VT125 ReGIS graphics are fixed at the upper left of the display; need to verify */ 1701894e0ac8Smrg 1702894e0ac8Smrg FOR_EACH_SLOT(ii) { 17032e4f8982Smrg Graphic *graphic; 17042e4f8982Smrg 1705894e0ac8Smrg if (!(graphic = getActiveSlot(ii))) 1706e0a2b6dfSmrg continue; 170701037d57Smrg if (graphic->bufferid != screen->whichBuf) 170801037d57Smrg continue; 17092e4f8982Smrg if (graphic->hidden) 17102e4f8982Smrg continue; 1711e0a2b6dfSmrg 1712e0a2b6dfSmrg graphic->charrow -= rows; 1713e0a2b6dfSmrg } 1714e0a2b6dfSmrg} 1715e0a2b6dfSmrg 1716894e0ac8Smrgvoid 1717e0a2b6dfSmrgpixelarea_clear_displayed_graphics(TScreen const *screen, 1718e0a2b6dfSmrg int winx, 1719e0a2b6dfSmrg int winy, 1720e0a2b6dfSmrg int w, 1721e0a2b6dfSmrg int h) 1722e0a2b6dfSmrg{ 1723894e0ac8Smrg unsigned ii; 1724e0a2b6dfSmrg 1725894e0ac8Smrg FOR_EACH_SLOT(ii) { 172601037d57Smrg Graphic *graphic; 172701037d57Smrg /* FIXME: are these coordinates (scrolled) screen-relative? */ 172801037d57Smrg int const scroll_y = (screen->whichBuf == 0 172901037d57Smrg ? screen->topline * FontHeight(screen) 173001037d57Smrg : 0); 173101037d57Smrg int graph_x; 173201037d57Smrg int graph_y; 173301037d57Smrg int x, y; 173401037d57Smrg 1735894e0ac8Smrg if (!(graphic = getActiveSlot(ii))) 1736e0a2b6dfSmrg continue; 173701037d57Smrg if (graphic->bufferid != screen->whichBuf) 173801037d57Smrg continue; 17392e4f8982Smrg if (graphic->hidden) 17402e4f8982Smrg continue; 1741e0a2b6dfSmrg 174201037d57Smrg graph_x = graphic->charcol * FontWidth(screen); 174301037d57Smrg graph_y = graphic->charrow * FontHeight(screen); 174401037d57Smrg x = winx - graph_x; 174501037d57Smrg y = (winy - scroll_y) - graph_y; 1746e0a2b6dfSmrg 174701037d57Smrg TRACE(("pixelarea clear graphics: screen->topline=%d winx=%d winy=%d w=%d h=%d x=%d y=%d\n", 1748e0a2b6dfSmrg screen->topline, 1749e0a2b6dfSmrg winx, winy, 1750e0a2b6dfSmrg w, h, 1751e0a2b6dfSmrg x, y)); 1752894e0ac8Smrg erase_graphic(graphic, x, y, w, h); 1753e0a2b6dfSmrg } 1754e0a2b6dfSmrg} 1755e0a2b6dfSmrg 1756894e0ac8Smrgvoid 1757e0a2b6dfSmrgchararea_clear_displayed_graphics(TScreen const *screen, 1758e0a2b6dfSmrg int leftcol, 1759e0a2b6dfSmrg int toprow, 1760e0a2b6dfSmrg int ncols, 1761e0a2b6dfSmrg int nrows) 1762e0a2b6dfSmrg{ 176301037d57Smrg int const x = leftcol * FontWidth(screen); 176401037d57Smrg int const y = toprow * FontHeight(screen); 176501037d57Smrg int const w = ncols * FontWidth(screen); 176601037d57Smrg int const h = nrows * FontHeight(screen); 1767e0a2b6dfSmrg 1768e0a2b6dfSmrg TRACE(("chararea clear graphics: screen->topline=%d leftcol=%d toprow=%d nrows=%d ncols=%d x=%d y=%d w=%d h=%d\n", 1769e0a2b6dfSmrg screen->topline, 1770e0a2b6dfSmrg leftcol, toprow, 1771e0a2b6dfSmrg nrows, ncols, 1772e0a2b6dfSmrg x, y, w, h)); 1773e0a2b6dfSmrg pixelarea_clear_displayed_graphics(screen, x, y, w, h); 1774e0a2b6dfSmrg} 1775e0a2b6dfSmrg 1776894e0ac8Smrgvoid 1777e0a2b6dfSmrgreset_displayed_graphics(TScreen const *screen) 1778e0a2b6dfSmrg{ 1779894e0ac8Smrg unsigned ii; 1780894e0ac8Smrg 1781894e0ac8Smrg init_color_registers(getSharedRegisters(), screen->terminal_id); 1782894e0ac8Smrg 1783894e0ac8Smrg TRACE(("resetting all graphics\n")); 1784894e0ac8Smrg FOR_EACH_SLOT(ii) { 1785894e0ac8Smrg deactivateSlot(ii); 1786894e0ac8Smrg } 1787894e0ac8Smrg} 1788894e0ac8Smrg 1789894e0ac8Smrg#ifdef NO_LEAKS 1790894e0ac8Smrgvoid 1791894e0ac8Smrgnoleaks_graphics(void) 1792894e0ac8Smrg{ 1793894e0ac8Smrg unsigned ii; 1794894e0ac8Smrg 1795894e0ac8Smrg FOR_EACH_SLOT(ii) { 1796894e0ac8Smrg deactivateSlot(ii); 1797e0a2b6dfSmrg } 1798e0a2b6dfSmrg} 1799894e0ac8Smrg#endif 1800