graphics.c revision 01037d57
11.20Slha/* $XTermId: graphics.c,v 1.62 2014/12/23 00:08:58 Ross.Combs Exp $ */ 21.9Smikel 31.1Scgd/* 41.18Slukem * Copyright 2013,2014 by Ross Combs 51.11Scgd * 61.1Scgd * All Rights Reserved 71.20Slha * 81.14Sjdolecek * Permission is hereby granted, free of charge, to any person obtaining a 91.20Slha * copy of this software and associated documentation files (the 101.1Scgd * "Software"), to deal in the Software without restriction, including 111.15Sis * without limitation the rights to use, copy, modify, merge, publish, 121.18Slukem * distribute, sublicense, and/or sell copies of the Software, and to 131.18Slukem * permit persons to whom the Software is furnished to do so, subject to 141.18Slukem * the following conditions: 151.18Slukem * 161.18Slukem * The above copyright notice and this permission notice shall be included 171.13Smrg * in all copies or substantial portions of the Software. 181.1Scgd * 191.1Scgd * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 20 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 22 * IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY 23 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 24 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 25 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 26 * 27 * Except as contained in this notice, the name(s) of the above copyright 28 * holders shall not be used in advertising or otherwise to promote the 29 * sale, use or other dealings in this Software without prior written 30 * authorization. 31 */ 32 33#include <xterm.h> 34 35#include <stdio.h> 36#include <ctype.h> 37#include <stdlib.h> 38 39#include <data.h> 40#include <ptyx.h> 41 42#include <assert.h> 43#include <graphics.h> 44 45#undef DUMP_BITMAP 46#undef DUMP_COLORS 47#undef DEBUG_PALETTE 48#undef DEBUG_PIXEL 49#undef DEBUG_REFRESH 50 51/* 52 * graphics TODO list 53 * 54 * ReGIS: 55 * - find a suitable default alphabet zero font instead of scaling Xft fonts 56 * - input and output cursors 57 * - mouse input 58 * - investigate second graphic page for ReGIS -- does it also apply to text and sixel graphics? are the contents preserved? 59 * - fix interpolated curves to more closely match implementation (identical despite direction and starting point) 60 * - non-ASCII alphabets 61 * - enter/leave during a command 62 * - command display mode 63 * - re-rasterization on resize 64 * - macros 65 * - improved fills for narrow angles (track actual lines not just pixels) 66 * 67 * sixel: 68 * - fix problem where new_row < 0 during sixel parsing (see FIXME) 69 * - screen-capture support (need dialog of some sort for safety) 70 * 71 * VT55/VT105 waveform graphics 72 * - everything 73 * 74 * Tektronix: 75 * - color (VT340 4014 emulation, 41xx, IRAF GTERM, and also MS-DOS Kermit color support) 76 * - polygon fill (41xx) 77 * - clear area extension 78 * - area fill extension 79 * - pixel operations (RU/RS/RP) 80 * - research other 41xx and 42xx extensions 81 * 82 * common graphics features: 83 * - handle light/dark screen modes (CSI?5[hl]) 84 * - update text fg/bg color which overlaps images 85 * - handle graphic updates in scroll regions (verify effect on graphics) 86 * - handle rectangular area copies (verify they work with graphics) 87 * - invalidate graphics under graphic if same origin, at least as big, and bg not transparent 88 * - invalidate graphic if completely scrolled past end of scrollback 89 * - invalidate graphic if all pixels are transparent/erased 90 * - invalidate graphic if completely scrolled out of alt buffer 91 * - posturize requested colors to match hardware palettes (e.g. only four possible shades on VT240) 92 * - color register report/restore 93 * - ability to select/copy graphics for pasting in other programs 94 * - ability to show non-scroll-mode sixel graphics in a separate window 95 * - ability to show ReGIS graphics in a separate window 96 * - ability to show Tektronix graphics in VT100 window 97 * - truncate graphics at bottom edge of window? 98 * 99 * new escape sequences: 100 * - way to query text font size without "window ops" (or make "window ops" permissions more fine grained) 101 * - way to query and set the number of graphics pages 102 * 103 * ReGIS extensions: 104 * - non-integer text scaling 105 * - free distortionless text rotation 106 * - font characteristics: bold/underline/italic 107 * - remove/increase arbitrary limits (pattern size, pages, alphabets, stack size, font names, etc.) 108 * - comment command 109 * - shade/fill with borders 110 * - sprites (copy portion of page into/out of buffer with scaling and rotation) 111 * - ellipses 112 * - 2D patterns 113 * - option to set actual graphic size (not just coordinate range) 114 * - gradients (for lines and fills) 115 * - line width (RLogin has this and it is mentioned in docs for the DEC ReGIS to Postscript converter) 116 * - transparency 117 * - background color as stackable write control 118 * - true color (virtual color registers created upon lookup) 119 * - anti-aliasing 120 * - variable-width text 121 */ 122 123/* font sizes: 124 * VT510: 125 * 80 Columns 132 Columns Maximum Number of Lines 126 * 10 x 16 6 x 16 26 lines + keyboard indicator line 127 * 10 x 13 6 x 13 26 lines + keyboard indicator line 128 * 10 x 10 6 x 10 42 lines + keyboard indicator line 129 * 10 x 8 6 x 8 53 lines + keyboard indicator line 130 */ 131 132typedef struct allocated_color_register { 133 struct allocated_color_register *next; 134 Pixel pix; 135 short r, g, b; 136} AllocatedColorRegister; 137 138#define LOOKUP_WIDTH 16 139static AllocatedColorRegister *allocated_colors[LOOKUP_WIDTH][LOOKUP_WIDTH][LOOKUP_WIDTH]; 140 141#define FOR_EACH_SLOT(ii) for (ii = 0U; ii < MAX_GRAPHICS; ii++) 142 143static ColorRegister *shared_color_registers; 144static Graphic *displayed_graphics[MAX_GRAPHICS]; 145static unsigned next_graphic_id = 0U; 146 147static ColorRegister * 148allocRegisters(void) 149{ 150 return TypeCallocN(ColorRegister, MAX_COLOR_REGISTERS); 151} 152 153static Graphic * 154freeGraphic(Graphic *obj) 155{ 156 if (obj) { 157 if (obj->pixels) 158 free(obj->pixels); 159 if (obj->private_color_registers) 160 free(obj->private_color_registers); 161 free(obj); 162 } 163 return NULL; 164} 165 166static Graphic * 167allocGraphic(int max_w, int max_h) 168{ 169 Graphic *result = TypeCalloc(Graphic); 170 if (result) { 171 result->max_width = max_w; 172 result->max_height = max_h; 173 if (!(result->pixels = TypeCallocN(RegisterNum, 174 (size_t) max_w * (size_t) max_h))) { 175 result = freeGraphic(result); 176 } else if (!(result->private_color_registers = allocRegisters())) { 177 result = freeGraphic(result); 178 } 179 } 180 return result; 181} 182 183static Graphic * 184getActiveSlot(unsigned n) 185{ 186 if (n < MAX_GRAPHICS && 187 displayed_graphics[n] && 188 displayed_graphics[n]->valid) { 189 return displayed_graphics[n]; 190 } 191 return NULL; 192} 193 194static Graphic * 195getInactiveSlot(const TScreen *screen, unsigned n) 196{ 197 if (n < MAX_GRAPHICS && 198 (!displayed_graphics[n] || 199 !displayed_graphics[n]->valid)) { 200 if (!displayed_graphics[n]) { 201 displayed_graphics[n] = allocGraphic(screen->graphics_max_wide, 202 screen->graphics_max_high); 203 } 204 return displayed_graphics[n]; 205 } 206 return NULL; 207} 208 209static ColorRegister * 210getSharedRegisters(void) 211{ 212 if (!shared_color_registers) 213 shared_color_registers = allocRegisters(); 214 return shared_color_registers; 215} 216 217static void 218deactivateSlot(unsigned n) 219{ 220 if (n < MAX_GRAPHICS) { 221 displayed_graphics[n] = freeGraphic(displayed_graphics[n]); 222 } 223} 224 225extern RegisterNum 226read_pixel(Graphic *graphic, int x, int y) 227{ 228 if (x < 0 || x >= graphic->actual_width || 229 y < 0 || y >= graphic->actual_height) { 230 return COLOR_HOLE; 231 } 232 233 return graphic->pixels[y * graphic->max_width + x]; 234} 235 236#define _draw_pixel(G, X, Y, C) \ 237 do { \ 238 (G)->pixels[(Y) * (G)->max_width + (X)] = (RegisterNum) (C); \ 239 } while (0) 240 241void 242draw_solid_pixel(Graphic *graphic, int x, int y, unsigned color) 243{ 244 assert(color <= MAX_COLOR_REGISTERS); 245 246#ifdef DEBUG_PIXEL 247 TRACE(("drawing pixel at %d,%d color=%hu (hole=%hu, [%d,%d,%d])\n", 248 x, 249 y, 250 color, 251 COLOR_HOLE, 252 ((color != COLOR_HOLE) 253 ? (unsigned) graphic->color_registers[color].r : 0U), 254 ((color != COLOR_HOLE) 255 ? (unsigned) graphic->color_registers[color].g : 0U), 256 ((color != COLOR_HOLE) 257 ? (unsigned) graphic->color_registers[color].b : 0U))); 258#endif 259 if (x >= 0 && x < graphic->actual_width && 260 y >= 0 && y < graphic->actual_height) { 261 _draw_pixel(graphic, x, y, color); 262 if (color < MAX_COLOR_REGISTERS) 263 graphic->color_registers_used[color] = 1; 264 } 265} 266 267void 268draw_solid_rectangle(Graphic *graphic, int x1, int y1, int x2, int y2, unsigned color) 269{ 270 int x, y; 271 int tmp; 272 273 assert(color <= MAX_COLOR_REGISTERS); 274 275 if (x1 > x2) { 276 EXCHANGE(x1, x2, tmp); 277 } 278 if (y1 > y2) { 279 EXCHANGE(y1, y2, tmp); 280 } 281 282 if (x2 < 0 || x1 >= graphic->actual_width || 283 y2 < 0 || y1 >= graphic->actual_height) 284 return; 285 286 if (x1 < 0) 287 x1 = 0; 288 if (x2 >= graphic->actual_width) 289 x2 = graphic->actual_width - 1; 290 if (y1 < 0) 291 y1 = 0; 292 if (y2 >= graphic->actual_height) 293 y2 = graphic->actual_height - 1; 294 295 if (color < MAX_COLOR_REGISTERS) 296 graphic->color_registers_used[color] = 1; 297 for (y = y1; y <= y2; y++) 298 for (x = x1; x <= x2; x++) 299 _draw_pixel(graphic, x, y, color); 300} 301 302void 303draw_solid_line(Graphic *graphic, int x1, int y1, int x2, int y2, unsigned color) 304{ 305 int x, y; 306 int dx, dy; 307 int dir, diff; 308 309 assert(color <= MAX_COLOR_REGISTERS); 310 311 dx = abs(x1 - x2); 312 dy = abs(y1 - y2); 313 314 if (dx > dy) { 315 if (x1 > x2) { 316 int tmp; 317 EXCHANGE(x1, x2, tmp); 318 EXCHANGE(y1, y2, tmp); 319 } 320 if (y1 < y2) 321 dir = 1; 322 else if (y1 > y2) 323 dir = -1; 324 else 325 dir = 0; 326 327 diff = 0; 328 y = y1; 329 for (x = x1; x <= x2; x++) { 330 if (diff >= dx) { 331 diff -= dx; 332 y += dir; 333 } 334 diff += dy; 335 draw_solid_pixel(graphic, x, y, color); 336 } 337 } else { 338 if (y1 > y2) { 339 int tmp; 340 EXCHANGE(x1, x2, tmp); 341 EXCHANGE(y1, y2, tmp); 342 } 343 if (x1 < x2) 344 dir = 1; 345 else if (x1 > x2) 346 dir = -1; 347 else 348 dir = 0; 349 350 diff = 0; 351 x = x1; 352 for (y = y1; y <= y2; y++) { 353 if (diff >= dy) { 354 diff -= dy; 355 x += dir; 356 } 357 diff += dx; 358 draw_solid_pixel(graphic, x, y, color); 359 } 360 } 361} 362 363void 364copy_overlapping_area(Graphic *graphic, int src_ul_x, int src_ul_y, 365 int dst_ul_x, int dst_ul_y, unsigned w, unsigned h, 366 unsigned default_color) 367{ 368 int sx, ex, dx; 369 int sy, ey, dy; 370 int xx, yy; 371 int dst_x, dst_y; 372 int src_x, src_y; 373 RegisterNum color; 374 375 if (dst_ul_x <= src_ul_x) { 376 sx = 0; 377 ex = (int) w - 1; 378 dx = +1; 379 } else { 380 sx = (int) w - 1; 381 ex = 0; 382 dx = -1; 383 } 384 385 if (dst_ul_y <= src_ul_y) { 386 sy = 0; 387 ey = (int) h - 1; 388 dy = +1; 389 } else { 390 sy = (int) h - 1; 391 ey = 0; 392 dy = -1; 393 } 394 395 for (yy = sy; yy != ey + dy; yy += dy) { 396 dst_y = dst_ul_y + yy; 397 src_y = src_ul_y + yy; 398 if (dst_y < 0 || dst_y >= (int) graphic->actual_height) 399 continue; 400 401 for (xx = sx; xx != ex + dx; xx += dx) { 402 dst_x = dst_ul_x + xx; 403 src_x = src_ul_x + xx; 404 if (dst_x < 0 || dst_x >= (int) graphic->actual_width) 405 continue; 406 407 if (src_x < 0 || src_x >= (int) graphic->actual_width || 408 src_y < 0 || src_y >= (int) graphic->actual_height) 409 color = (RegisterNum) default_color; 410 else 411 color = graphic->pixels[(unsigned) (src_y * 412 graphic->max_width) + 413 (unsigned) src_x]; 414 415 graphic->pixels[(unsigned) (dst_y * graphic->max_width) + 416 (unsigned) dst_x] = color; 417 } 418 } 419} 420 421static void 422set_color_register(ColorRegister *color_registers, 423 unsigned color, 424 int r, 425 int g, 426 int b) 427{ 428 ColorRegister *reg = &color_registers[color]; 429 reg->r = (short) r; 430 reg->g = (short) g; 431 reg->b = (short) b; 432} 433 434/* Graphics which don't use private colors will act as if they are using a 435 * device-wide color palette. 436 */ 437static void 438set_shared_color_register(unsigned color, int r, int g, int b) 439{ 440 Graphic *graphic; 441 unsigned ii; 442 443 assert(color < MAX_COLOR_REGISTERS); 444 445 set_color_register(getSharedRegisters(), color, r, g, b); 446 447 FOR_EACH_SLOT(ii) { 448 if (!(graphic = getActiveSlot(ii))) 449 continue; 450 if (graphic->private_colors) 451 continue; 452 453 if (graphic->color_registers_used[ii]) { 454 graphic->dirty = 1; 455 } 456 } 457} 458 459void 460update_color_register(Graphic *graphic, 461 unsigned color, 462 int r, 463 int g, 464 int b) 465{ 466 assert(color < MAX_COLOR_REGISTERS); 467 468 if (graphic->private_colors) { 469 set_color_register(graphic->private_color_registers, 470 color, r, g, b); 471 if (graphic->color_registers_used[color]) { 472 graphic->dirty = 1; 473 } 474 graphic->color_registers_used[color] = 1; 475 } else { 476 set_shared_color_register(color, r, g, b); 477 } 478} 479 480#define SQUARE(X) ( (X) * (X) ) 481 482RegisterNum 483find_color_register(ColorRegister const *color_registers, int r, int g, int b) 484{ 485 unsigned i; 486 unsigned d; 487 unsigned closest_index; 488 unsigned closest_distance; 489 490 /* I have no idea what algorithm DEC used for this. 491 * The documentation warns that it is unpredictable, especially with values 492 * far away from any allocated color so it is probably a very simple 493 * heuristic rather than something fancy like finding the minimum distance 494 * in a linear perceptive color space. 495 */ 496 closest_index = MAX_COLOR_REGISTERS; 497 closest_distance = 0U; 498 for (i = 0U; i < MAX_COLOR_REGISTERS; i++) { 499 d = (unsigned) (SQUARE(2 * (color_registers[i].r - r)) + 500 SQUARE(3 * (color_registers[i].g - g)) + 501 SQUARE(1 * (color_registers[i].b - b))); 502 if (closest_index == MAX_COLOR_REGISTERS || d < closest_distance) { 503 closest_index = i; 504 closest_distance = d; 505 } 506 } 507 508 TRACE(("found closest color register to %d,%d,%d: %u (distance %u value %d,%d,%d)\n", 509 r, g, b, 510 closest_index, 511 closest_distance, 512 color_registers[closest_index].r, 513 color_registers[closest_index].g, 514 color_registers[closest_index].b)); 515 return (RegisterNum) closest_index; 516} 517 518static void 519init_color_registers(ColorRegister *color_registers, int terminal_id) 520{ 521 TRACE(("setting initial colors for terminal %d\n", terminal_id)); 522 { 523 unsigned i; 524 525 for (i = 0U; i < MAX_COLOR_REGISTERS; i++) { 526 set_color_register(color_registers, (RegisterNum) i, 0, 0, 0); 527 } 528 } 529 530 /* 531 * default color registers: 532 * (mono) (color) 533 * VK100/GIGI (fixed) 534 * VT125: 535 * 0: 0% 0% 536 * 1: 33% blue 537 * 2: 66% red 538 * 3: 100% green 539 * VT240: 540 * 0: 0% 0% 541 * 1: 33% blue 542 * 2: 66% red 543 * 3: 100% green 544 * VT241: 545 * 0: 0% 0% 546 * 1: 33% blue 547 * 2: 66% red 548 * 3: 100% green 549 * VT330: 550 * 0: 0% 0% (bg for light on dark mode) 551 * 1: 33% blue (red?) 552 * 2: 66% red (green?) 553 * 3: 100% green (yellow?) (fg for light on dark mode) 554 * VT340: 555 * 0: 0% 0% (bg for light on dark mode) 556 * 1: 14% blue 557 * 2: 29% red 558 * 3: 43% green 559 * 4: 57% magenta 560 * 5: 71% cyan 561 * 6: 86% yellow 562 * 7: 100% 50% (fg for light on dark mode) 563 * 8: 0% 25% 564 * 9: 14% gray-blue 565 * 10: 29% gray-red 566 * 11: 43% gray-green 567 * 12: 57% gray-magenta 568 * 13: 71% gray-cyan 569 * 14: 86% gray-yellow 570 * 15: 100% 75% ("white") 571 * VT382: 572 * ? (FIXME: B&W only?) 573 * dxterm: 574 * ? 575 */ 576 switch (terminal_id) { 577 case 125: 578 case 241: 579 set_color_register(color_registers, 0, 0, 0, 0); 580 set_color_register(color_registers, 1, 0, 0, 100); 581 set_color_register(color_registers, 2, 0, 100, 0); 582 set_color_register(color_registers, 3, 100, 0, 0); 583 break; 584 case 240: 585 case 330: 586 set_color_register(color_registers, 0, 0, 0, 0); 587 set_color_register(color_registers, 1, 33, 33, 33); 588 set_color_register(color_registers, 2, 66, 66, 66); 589 set_color_register(color_registers, 3, 100, 100, 100); 590 break; 591 case 340: 592 default: 593 set_color_register(color_registers, 0, 0, 0, 0); 594 set_color_register(color_registers, 1, 20, 20, 80); 595 set_color_register(color_registers, 2, 80, 13, 13); 596 set_color_register(color_registers, 3, 20, 80, 20); 597 set_color_register(color_registers, 4, 80, 20, 80); 598 set_color_register(color_registers, 5, 20, 80, 80); 599 set_color_register(color_registers, 6, 80, 80, 20); 600 set_color_register(color_registers, 7, 53, 53, 53); 601 set_color_register(color_registers, 8, 26, 26, 26); 602 set_color_register(color_registers, 9, 33, 33, 60); 603 set_color_register(color_registers, 10, 60, 26, 26); 604 set_color_register(color_registers, 11, 33, 60, 33); 605 set_color_register(color_registers, 12, 60, 33, 60); 606 set_color_register(color_registers, 13, 33, 60, 60); 607 set_color_register(color_registers, 14, 60, 60, 33); 608 set_color_register(color_registers, 15, 80, 80, 80); 609 break; 610 case 382: /* FIXME: verify */ 611 set_color_register(color_registers, 0, 0, 0, 0); 612 set_color_register(color_registers, 1, 100, 100, 100); 613 break; 614 } 615 616#ifdef DEBUG_PALETTE 617 { 618 unsigned i; 619 620 for (i = 0U; i < MAX_COLOR_REGISTERS; i++) { 621 TRACE(("initial value for register %03u: %d,%d,%d\n", 622 i, 623 color_registers[i].r, 624 color_registers[i].g, 625 color_registers[i].b)); 626 } 627 } 628#endif 629} 630 631unsigned 632get_color_register_count(TScreen const *screen) 633{ 634 unsigned num_color_registers; 635 636 if (screen->numcolorregisters >= 0) { 637 num_color_registers = (unsigned) screen->numcolorregisters; 638 } else { 639 num_color_registers = 0U; 640 } 641 642 if (num_color_registers > 1U) { 643 if (num_color_registers > MAX_COLOR_REGISTERS) 644 return MAX_COLOR_REGISTERS; 645 return num_color_registers; 646 } 647 648 /* 649 * color capabilities: 650 * VK100/GIGI 1 plane (12x1 pixel attribute blocks) colorspace is 8 fixed colors (black, white, red, green, blue, cyan, yellow, magenta) 651 * VT125 2 planes (4 registers) colorspace is (64?) (color), ? (grayscale) 652 * VT240 2 planes (4 registers) colorspace is 4 shades (grayscale) 653 * VT241 2 planes (4 registers) colorspace is ? (color), ? shades (grayscale) 654 * VT330 2 planes (4 registers) colorspace is 4 shades (grayscale) 655 * VT340 4 planes (16 registers) colorspace is r16g16b16 (color), 16 shades (grayscale) 656 * VT382 1 plane (two fixed colors: black and white) FIXME: verify 657 * dxterm ? 658 */ 659 switch (screen->terminal_id) { 660 case 125: 661 return 4U; 662 case 240: 663 return 4U; 664 case 241: 665 return 4U; 666 case 330: 667 return 4U; 668 case 340: 669 return 16U; 670 case 382: 671 return 2U; 672 default: 673 /* unknown graphics model -- might as well be generous */ 674 return MAX_COLOR_REGISTERS; 675 } 676} 677 678static void 679init_graphic(Graphic *graphic, 680 unsigned type, 681 int terminal_id, 682 int charrow, 683 int charcol, 684 unsigned num_color_registers, 685 int private_colors) 686{ 687 const unsigned max_pixels = (unsigned) (graphic->max_width * 688 graphic->max_height); 689 unsigned i; 690 691 TRACE(("initializing graphic object\n")); 692 693 graphic->dirty = 1; 694 for (i = 0U; i < max_pixels; i++) 695 graphic->pixels[i] = COLOR_HOLE; 696 memset(graphic->color_registers_used, 0, sizeof(graphic->color_registers_used)); 697 698 /* 699 * text and graphics interactions: 700 * VK100/GIGI text writes on top of graphics buffer, color attribute shared with text 701 * VT240,VT241,VT330,VT340 text writes on top of graphics buffer 702 * VT382 text writes on top of graphics buffer FIXME: verify 703 * VT125 graphics buffer overlaid on top of text in B&W display, text not present in color display 704 */ 705 706 /* 707 * dimensions (ReGIS logical, physical): 708 * VK100/GIGI 768x4?? 768x240(status?) 709 * VT125 768x460 768x230(+10status) (1:2 aspect ratio, ReGIS halves vertical addresses through "odd y emulation") 710 * VT240 800x460 800x230(+10status) (1:2 aspect ratio, ReGIS halves vertical addresses through "odd y emulation") 711 * VT241 800x460 800x230(+10status) (1:2 aspect ratio, ReGIS halves vertical addresses through "odd y emulation") 712 * VT330 800x480 800x480(+?status) 713 * VT340 800x480 800x480(+?status) 714 * VT382 960x750 sixel only 715 * dxterm ?x? ?x? variable? 716 */ 717 718 graphic->actual_width = 0; 719 graphic->actual_height = 0; 720 721 graphic->pixw = 1; 722 graphic->pixh = 1; 723 724 graphic->valid_registers = num_color_registers; 725 TRACE(("%d color registers\n", graphic->valid_registers)); 726 727 graphic->private_colors = private_colors; 728 if (graphic->private_colors) { 729 TRACE(("using private color registers\n")); 730 init_color_registers(graphic->private_color_registers, terminal_id); 731 graphic->color_registers = graphic->private_color_registers; 732 } else { 733 TRACE(("using shared color registers\n")); 734 graphic->color_registers = getSharedRegisters(); 735 } 736 737 graphic->charrow = charrow; 738 graphic->charcol = charcol; 739 graphic->type = type; 740 graphic->valid = 0; 741} 742 743Graphic * 744get_new_graphic(XtermWidget xw, int charrow, int charcol, unsigned type) 745{ 746 TScreen const *screen = TScreenOf(xw); 747 int bufferid = screen->whichBuf; 748 int terminal_id = screen->terminal_id; 749 Graphic *graphic; 750 unsigned ii; 751 752 FOR_EACH_SLOT(ii) { 753 if ((graphic = getInactiveSlot(screen, ii))) { 754 TRACE(("using fresh graphic index=%u id=%u\n", ii, next_graphic_id)); 755 break; 756 } 757 } 758 759 /* if none are free, recycle the graphic scrolled back the farthest */ 760 if (!graphic) { 761 int min_charrow = 0; 762 Graphic *min_graphic = NULL; 763 764 FOR_EACH_SLOT(ii) { 765 if (!(graphic = getActiveSlot(ii))) 766 continue; 767 if (!min_graphic || graphic->charrow < min_charrow) { 768 min_charrow = graphic->charrow; 769 min_graphic = graphic; 770 } 771 } 772 TRACE(("recycling old graphic index=%u id=%u\n", ii, next_graphic_id)); 773 graphic = min_graphic; 774 } 775 776 if (graphic) { 777 unsigned num_color_registers; 778 num_color_registers = get_color_register_count(screen); 779 graphic->xw = xw; 780 graphic->bufferid = bufferid; 781 graphic->id = next_graphic_id++; 782 init_graphic(graphic, 783 type, 784 terminal_id, 785 charrow, 786 charcol, 787 num_color_registers, 788 screen->privatecolorregisters); 789 } 790 return graphic; 791} 792 793Graphic * 794get_new_or_matching_graphic(XtermWidget xw, 795 int charrow, 796 int charcol, 797 int actual_width, 798 int actual_height, 799 unsigned type) 800{ 801 TScreen const *screen = TScreenOf(xw); 802 int bufferid = screen->whichBuf; 803 Graphic *graphic; 804 unsigned ii; 805 806 FOR_EACH_SLOT(ii) { 807 if ((graphic = getActiveSlot(ii)) && 808 graphic->type == type && 809 graphic->bufferid == bufferid && 810 graphic->charrow == charrow && 811 graphic->charcol == charcol && 812 graphic->actual_width == actual_width && 813 graphic->actual_height == actual_height) { 814 TRACE(("found existing graphic index=%u id=%u\n", ii, graphic->id)); 815 return graphic; 816 } 817 } 818 819 /* if no match get a new graphic */ 820 if ((graphic = get_new_graphic(xw, charrow, charcol, type))) { 821 graphic->actual_width = actual_width; 822 graphic->actual_height = actual_height; 823 } 824 return graphic; 825} 826 827static int 828lookup_allocated_color(const ColorRegister *reg, Pixel *pix) 829{ 830 unsigned const rr = ((unsigned) reg->r * (LOOKUP_WIDTH - 1)) / CHANNEL_MAX; 831 unsigned const gg = ((unsigned) reg->g * (LOOKUP_WIDTH - 1)) / CHANNEL_MAX; 832 unsigned const bb = ((unsigned) reg->b * (LOOKUP_WIDTH - 1)) / CHANNEL_MAX; 833 const AllocatedColorRegister *search; 834 835 for (search = allocated_colors[rr][gg][bb]; search; search = search->next) { 836 if (search->r == reg->r && 837 search->g == reg->g && 838 search->b == reg->b) { 839 *pix = search->pix; 840 return 1; 841 } 842 } 843 844 *pix = 0UL; 845 return 0; 846} 847 848#define ScaleForXColor(s) (unsigned short) ((long)(s) * 65535 / CHANNEL_MAX) 849 850static int 851save_allocated_color(const ColorRegister *reg, XtermWidget xw, Pixel *pix) 852{ 853 unsigned const rr = ((unsigned) reg->r * (LOOKUP_WIDTH - 1)) / CHANNEL_MAX; 854 unsigned const gg = ((unsigned) reg->g * (LOOKUP_WIDTH - 1)) / CHANNEL_MAX; 855 unsigned const bb = ((unsigned) reg->b * (LOOKUP_WIDTH - 1)) / CHANNEL_MAX; 856 XColor xcolor; 857 AllocatedColorRegister *new_color; 858 859 xcolor.pixel = 0UL; 860 xcolor.red = ScaleForXColor(reg->r); 861 xcolor.green = ScaleForXColor(reg->g); 862 xcolor.blue = ScaleForXColor(reg->b); 863 xcolor.flags = DoRed | DoGreen | DoBlue; 864 if (!allocateBestRGB(xw, &xcolor)) { 865 TRACE(("unable to allocate xcolor\n")); 866 *pix = 0UL; 867 return 0; 868 } 869 870 *pix = xcolor.pixel; 871 872 if (!(new_color = malloc(sizeof(*new_color)))) { 873 TRACE(("unable to save pixel %lu\n", (unsigned long) *pix)); 874 return 0; 875 } 876 new_color->r = reg->r; 877 new_color->g = reg->g; 878 new_color->b = reg->b; 879 new_color->pix = *pix; 880 new_color->next = allocated_colors[rr][gg][bb]; 881 882 allocated_colors[rr][gg][bb] = new_color; 883 884 return 1; 885} 886 887static Pixel 888color_register_to_xpixel(const ColorRegister *reg, XtermWidget xw) 889{ 890 Pixel pix; 891 892 if (!lookup_allocated_color(reg, &pix)) 893 save_allocated_color(reg, xw, &pix); 894 895 /* FIXME: with so many possible colors we need to determine 896 * when to free them to be nice to PseudoColor displays 897 */ 898 return pix; 899} 900 901static void 902refresh_graphic(TScreen const *screen, 903 Graphic const *graphic, 904 ColorRegister *buffer, 905 int refresh_x, 906 int refresh_y, 907 int refresh_w, 908 int refresh_h, 909 int draw_x, 910 int draw_y, 911 int draw_w, 912 int draw_h) 913{ 914 int const pw = graphic->pixw; 915 int const ph = graphic->pixh; 916 int const graph_x = graphic->charcol * FontWidth(screen); 917 int const graph_y = graphic->charrow * FontHeight(screen); 918 int const graph_w = graphic->actual_width; 919 int const graph_h = graphic->actual_height; 920 int const mw = graphic->max_width; 921 int r, c; 922 int fillx, filly; 923 int holes, total, out_of_range; 924 RegisterNum regnum; 925 926 TRACE(("refreshing graphic %u from %d,%d %dx%d (valid=%d, size=%dx%d, scale=%dx%d max=%dx%d)\n", 927 graphic->id, 928 graph_x, graph_y, draw_w, draw_h, 929 graphic->valid, 930 graphic->actual_width, 931 graphic->actual_height, 932 pw, ph, 933 graphic->max_width, 934 graphic->max_height)); 935 936 TRACE(("refresh pixmap starts at %d,%d\n", refresh_x, refresh_y)); 937 938 holes = total = 0; 939 out_of_range = 0; 940 for (r = 0; r < graph_h; r++) { 941 int pmy = graph_y + r * ph; 942 943 if (pmy + ph - 1 < draw_y) 944 continue; 945 if (pmy > draw_y + draw_h - 1) 946 break; 947 948 for (c = 0; c < graph_w; c++) { 949 int pmx = graph_x + c * pw; 950 951 if (pmx + pw - 1 < draw_x) 952 continue; 953 if (pmx > draw_x + draw_w - 1) 954 break; 955 956 total++; 957 regnum = graphic->pixels[r * mw + c]; 958 if (regnum == COLOR_HOLE) { 959 holes++; 960 continue; 961 } 962 963 for (fillx = 0; fillx < pw; fillx++) { 964 for (filly = 0; filly < ph; filly++) { 965 if (pmx < draw_x || pmx > draw_x + draw_w - 1 || 966 pmy < draw_y || pmy > draw_y + draw_h - 1) { 967 out_of_range++; 968 continue; 969 } 970 971 /* this shouldn't happen, but it doesn't hurt to check */ 972 if (pmx < refresh_x || pmx > refresh_x + refresh_w - 1 || 973 pmy < refresh_y || pmy > refresh_y + refresh_h - 1) { 974 TRACE(("OUT OF RANGE: %d,%d (%d,%d)\n", pmx, pmy, r, c)); 975 out_of_range++; 976 continue; 977 } 978 979 buffer[(pmy - refresh_y) * refresh_w + 980 (pmx - refresh_x)] = 981 graphic->color_registers[regnum]; 982 } 983 } 984 } 985 } 986 987 TRACE(("done refreshing graphic: %d of %d refreshed pixels were holes; %d were out of pixmap range\n", 988 holes, total, out_of_range)); 989} 990 991#ifdef DEBUG_REFRESH 992 993#define BASEX(X) ( (draw_x - base_x) + (X) ) 994#define BASEY(Y) ( (draw_y - base_y) + (Y) ) 995 996static void 997outline_refresh(TScreen const *screen, 998 Graphic const *graphic, 999 Pixmap output_pm, 1000 GC graphics_gc, 1001 int base_x, 1002 int base_y, 1003 int draw_x, 1004 int draw_y, 1005 int draw_w, 1006 int draw_h) 1007{ 1008 Display *const display = screen->display; 1009 int const pw = graphic->pixw; 1010 int const ph = graphic->pixh; 1011 XGCValues xgcv; 1012 XColor def; 1013 1014 def.red = (unsigned short) ((1.0 - 0.1 * (rand() / (double) 1015 RAND_MAX) * 65535.0)); 1016 def.green = (unsigned short) ((0.7 + 0.2 * (rand() / (double) 1017 RAND_MAX)) * 65535.0); 1018 def.blue = (unsigned short) ((0.1 + 0.1 * (rand() / (double) 1019 RAND_MAX)) * 65535.0); 1020 def.flags = DoRed | DoGreen | DoBlue; 1021 if (allocateBestRGB(graphic->xw, &def)) { 1022 xgcv.foreground = def.pixel; 1023 XChangeGC(display, graphics_gc, GCForeground, &xgcv); 1024 } 1025 1026 XDrawLine(display, output_pm, graphics_gc, 1027 BASEX(0), BASEY(0), 1028 BASEX(draw_w - 1), BASEY(0)); 1029 XDrawLine(display, output_pm, graphics_gc, 1030 BASEX(0), BASEY(draw_h - 1), 1031 BASEX(draw_w - 1), BASEY(draw_h - 1)); 1032 1033 XDrawLine(display, output_pm, graphics_gc, 1034 BASEX(0), BASEY(0), 1035 BASEX(0), BASEY(draw_h - 1)); 1036 XDrawLine(display, output_pm, graphics_gc, 1037 BASEX(draw_w - 1), BASEY(0), 1038 BASEX(draw_w - 1), BASEY(draw_h - 1)); 1039 1040 XDrawLine(display, output_pm, graphics_gc, 1041 BASEX(draw_w - 1), BASEY(0), 1042 BASEX(0), BASEY(draw_h - 1)); 1043 XDrawLine(display, output_pm, graphics_gc, 1044 BASEX(draw_w - 1), BASEY(draw_h - 1), 1045 BASEX(0), BASEY(0)); 1046 1047 def.red = (short) (0.7 * 65535.0); 1048 def.green = (short) (0.1 * 65535.0); 1049 def.blue = (short) (1.0 * 65535.0); 1050 def.flags = DoRed | DoGreen | DoBlue; 1051 if (allocateBestRGB(graphic->xw, &def)) { 1052 xgcv.foreground = def.pixel; 1053 XChangeGC(display, graphics_gc, GCForeground, &xgcv); 1054 } 1055 XFillRectangle(display, output_pm, graphics_gc, 1056 BASEX(0), 1057 BASEY(0), 1058 (unsigned) pw, (unsigned) ph); 1059 XFillRectangle(display, output_pm, graphics_gc, 1060 BASEX(draw_w - 1 - pw), 1061 BASEY(draw_h - 1 - ph), 1062 (unsigned) pw, (unsigned) ph); 1063} 1064#endif 1065 1066/* 1067 * Primary color hues: 1068 * blue: 0 degrees 1069 * red: 120 degrees 1070 * green: 240 degrees 1071 */ 1072void 1073hls2rgb(int h, int l, int s, short *r, short *g, short *b) 1074{ 1075 const int hs = ((h + 240) / 60) % 6; 1076 const double lv = l / 100.0; 1077 const double sv = s / 100.0; 1078 double c, x, m, c2; 1079 double r1, g1, b1; 1080 1081 if (s == 0) { 1082 *r = *g = *b = (short) l; 1083 return; 1084 } 1085 1086 c2 = (2.0 * lv) - 1.0; 1087 if (c2 < 0.0) 1088 c2 = -c2; 1089 c = (1.0 - c2) * sv; 1090 x = (hs & 1) ? c : 0.0; 1091 m = lv - 0.5 * c; 1092 1093 switch (hs) { 1094 case 0: 1095 r1 = c; 1096 g1 = x; 1097 b1 = 0.0; 1098 break; 1099 case 1: 1100 r1 = x; 1101 g1 = c; 1102 b1 = 0.0; 1103 break; 1104 case 2: 1105 r1 = 0.0; 1106 g1 = c; 1107 b1 = x; 1108 break; 1109 case 3: 1110 r1 = 0.0; 1111 g1 = x; 1112 b1 = c; 1113 break; 1114 case 4: 1115 r1 = x; 1116 g1 = 0.0; 1117 b1 = c; 1118 break; 1119 case 5: 1120 r1 = c; 1121 g1 = 0.0; 1122 b1 = x; 1123 break; 1124 default: 1125 TRACE(("Bad HLS input: [%d,%d,%d], returning white\n", h, l, s)); 1126 *r = (short) 100; 1127 *g = (short) 100; 1128 *b = (short) 100; 1129 return; 1130 } 1131 1132 *r = (short) ((r1 + m) * 100.0 + 0.5); 1133 *g = (short) ((g1 + m) * 100.0 + 0.5); 1134 *b = (short) ((b1 + m) * 100.0 + 0.5); 1135 1136 if (*r < 0) 1137 *r = 0; 1138 else if (*r > 100) 1139 *r = 100; 1140 if (*g < 0) 1141 *g = 0; 1142 else if (*g > 100) 1143 *g = 100; 1144 if (*b < 0) 1145 *b = 0; 1146 else if (*b > 100) 1147 *b = 100; 1148} 1149 1150void 1151dump_graphic(Graphic const *graphic) 1152{ 1153#if defined(DUMP_COLORS) || defined(DUMP_BITMAP) 1154 RegisterNum color; 1155#endif 1156#ifdef DUMP_BITMAP 1157 int r, c; 1158 ColorRegister const *reg; 1159#endif 1160 1161 (void) graphic; 1162 1163 TRACE(("graphic stats: id=%u charrow=%d charcol=%d actual_width=%d actual_height=%d pixw=%d pixh=%d\n", 1164 graphic->id, 1165 graphic->charrow, 1166 graphic->charcol, 1167 graphic->actual_width, 1168 graphic->actual_height, 1169 graphic->pixw, 1170 graphic->pixh)); 1171 1172#ifdef DUMP_COLORS 1173 TRACE(("graphic colors:\n")); 1174 for (color = 0; color < graphic->valid_registers; color++) { 1175 TRACE(("%03u: %d,%d,%d\n", 1176 color, 1177 graphic->color_registers[color].r, 1178 graphic->color_registers[color].g, 1179 graphic->color_registers[color].b)); 1180 } 1181#endif 1182 1183#ifdef DUMP_BITMAP 1184 TRACE(("graphic pixels:\n")); 1185 for (r = 0; r < graphic->actual_height; r++) { 1186 for (c = 0; c < graphic->actual_width; c++) { 1187 color = graphic->pixels[r * graphic->max_width + c]; 1188 if (color == COLOR_HOLE) { 1189 TRACE(("?")); 1190 } else { 1191 reg = &graphic->color_registers[color]; 1192 if (reg->r + reg->g + reg->b > 200) { 1193 TRACE(("#")); 1194 } else if (reg->r + reg->g + reg->b > 150) { 1195 TRACE(("%%")); 1196 } else if (reg->r + reg->g + reg->b > 100) { 1197 TRACE((":")); 1198 } else if (reg->r + reg->g + reg->b > 80) { 1199 TRACE((".")); 1200 } else { 1201 TRACE((" ")); 1202 } 1203 } 1204 } 1205 TRACE(("\n")); 1206 } 1207 1208 TRACE(("\n")); 1209#endif 1210} 1211 1212/* Erase the portion of any displayed graphic overlapping with a rectangle 1213 * of the given size and location in pixels relative to the start of the 1214 * graphic. This is used to allow text to "erase" graphics underneath it. 1215 */ 1216static void 1217erase_graphic(Graphic *graphic, int x, int y, int w, int h) 1218{ 1219 RegisterNum hole = COLOR_HOLE; 1220 int pw, ph; 1221 int r, c; 1222 int rbase, cbase; 1223 1224 pw = graphic->pixw; 1225 ph = graphic->pixh; 1226 1227 TRACE(("erasing graphic %d,%d %dx%d\n", x, y, w, h)); 1228 1229 rbase = 0; 1230 for (r = 0; r < graphic->actual_height; r++) { 1231 if (rbase + ph - 1 >= y 1232 && rbase <= y + h - 1) { 1233 cbase = 0; 1234 for (c = 0; c < graphic->actual_width; c++) { 1235 if (cbase + pw - 1 >= x 1236 && cbase <= x + w - 1) { 1237 graphic->pixels[r * graphic->max_width + c] = hole; 1238 } 1239 cbase += pw; 1240 } 1241 } 1242 rbase += ph; 1243 } 1244} 1245 1246static int 1247compare_graphic_ids(const void *left, const void *right) 1248{ 1249 const Graphic *l = *(const Graphic *const *) left; 1250 const Graphic *r = *(const Graphic *const *) right; 1251 1252 if (!l->valid || !r->valid) 1253 return 0; 1254 1255 if (l->bufferid < r->bufferid) 1256 return -1; 1257 else if (l->bufferid > r->bufferid) 1258 return 1; 1259 1260 if (l->id < r->id) 1261 return -1; 1262 else 1263 return 1; 1264} 1265 1266static void 1267clip_area(int *orig_x, int *orig_y, int *orig_w, int *orig_h, 1268 int clip_x, int clip_y, int clip_w, int clip_h) 1269{ 1270 if (*orig_x < clip_x) { 1271 const int diff = clip_x - *orig_x; 1272 *orig_x += diff; 1273 *orig_w -= diff; 1274 } 1275 if (*orig_w > 0 && *orig_x + *orig_w > clip_x + clip_w) { 1276 *orig_w -= (*orig_x + *orig_w) - (clip_x + clip_w); 1277 } 1278 1279 if (*orig_y < clip_y) { 1280 const int diff = clip_y - *orig_y; 1281 *orig_y += diff; 1282 *orig_h -= diff; 1283 } 1284 if (*orig_h > 0 && *orig_y + *orig_h > clip_y + clip_h) { 1285 *orig_h -= (*orig_y + *orig_h) - (clip_y + clip_h); 1286 } 1287} 1288 1289/* the coordinates are relative to the screen */ 1290static void 1291refresh_graphics(XtermWidget xw, 1292 int leftcol, 1293 int toprow, 1294 int ncols, 1295 int nrows, 1296 int skip_clean) 1297{ 1298 TScreen *const screen = TScreenOf(xw); 1299 Display *const display = screen->display; 1300 Window const drawable = VDrawable(screen); 1301 int const scroll_y = screen->topline * FontHeight(screen); 1302 int const refresh_x = leftcol * FontWidth(screen); 1303 int const refresh_y = toprow * FontHeight(screen) + scroll_y; 1304 int const refresh_w = ncols * FontWidth(screen); 1305 int const refresh_h = nrows * FontHeight(screen); 1306 int draw_x_min, draw_x_max; 1307 int draw_y_min, draw_y_max; 1308 Graphic *ordered_graphics[MAX_GRAPHICS]; 1309 unsigned ii, jj; 1310 unsigned active_count; 1311 unsigned holes, non_holes; 1312 int xx, yy; 1313 ColorRegister *buffer; 1314 1315 active_count = 0; 1316 FOR_EACH_SLOT(ii) { 1317 Graphic *graphic; 1318 if (!(graphic = getActiveSlot(ii))) 1319 continue; 1320 TRACE(("refreshing graphic %d on buffer %d, current buffer %d\n", 1321 graphic->id, graphic->bufferid, screen->whichBuf)); 1322 if (screen->whichBuf == 0) { 1323 if (graphic->bufferid != 0) { 1324 TRACE(("skipping graphic %d from alt buffer (%d) when drawing screen=%d\n", 1325 graphic->id, graphic->bufferid, screen->whichBuf)); 1326 continue; 1327 } 1328 } else { 1329 if (graphic->bufferid == 0 && graphic->charrow >= 0) { 1330 TRACE(("skipping graphic %d from normal buffer (%d) when drawing screen=%d because it is not in scrollback area\n", 1331 graphic->id, graphic->bufferid, screen->whichBuf)); 1332 continue; 1333 } 1334 if (graphic->bufferid == 1 && 1335 graphic->charrow + (graphic->actual_height + 1336 FontHeight(screen) - 1) / 1337 FontHeight(screen) < 0) { 1338 TRACE(("skipping graphic %d from alt buffer (%d) when drawing screen=%d because it is completely in scrollback area\n", 1339 graphic->id, graphic->bufferid, screen->whichBuf)); 1340 continue; 1341 } 1342 } 1343 ordered_graphics[active_count++] = graphic; 1344 } 1345 1346 if (active_count == 0) 1347 return; 1348 if (active_count > 1) { 1349 qsort(ordered_graphics, 1350 (size_t) active_count, 1351 sizeof(ordered_graphics[0]), 1352 compare_graphic_ids); 1353 } 1354 1355 if (skip_clean) { 1356 unsigned skip_count; 1357 1358 for (jj = 0; jj < active_count; ++jj) { 1359 if (ordered_graphics[jj]->dirty) 1360 break; 1361 } 1362 skip_count = jj; 1363 if (skip_count == active_count) 1364 return; 1365 1366 active_count -= skip_count; 1367 for (jj = 0; jj < active_count; ++jj) { 1368 ordered_graphics[jj] = ordered_graphics[jj + skip_count]; 1369 } 1370 } 1371 1372 if (!(buffer = malloc(sizeof(*buffer) * 1373 (unsigned) refresh_w * (unsigned) refresh_h))) { 1374 TRACE(("unable to allocate %dx%d buffer for graphics refresh\n", 1375 refresh_w, refresh_h)); 1376 return; 1377 } 1378 for (yy = 0; yy < refresh_h; yy++) { 1379 for (xx = 0; xx < refresh_w; xx++) { 1380 buffer[yy * refresh_w + xx].r = -1; 1381 buffer[yy * refresh_w + xx].g = -1; 1382 buffer[yy * refresh_w + xx].b = -1; 1383 } 1384 } 1385 1386 TRACE(("refresh: screen->topline=%d leftcol=%d toprow=%d nrows=%d ncols=%d (%d,%d %dx%d)\n", 1387 screen->topline, 1388 leftcol, toprow, 1389 nrows, ncols, 1390 refresh_x, refresh_y, 1391 refresh_w, refresh_h)); 1392 1393 { 1394 int const altarea_x = 0; 1395 int const altarea_y = 0; 1396 int const altarea_w = Width(screen) * FontWidth(screen); 1397 int const altarea_h = Height(screen) * FontHeight(screen); 1398 1399 int const scrollarea_x = 0; 1400 int const scrollarea_y = scroll_y; 1401 int const scrollarea_w = Width(screen) * FontWidth(screen); 1402 int const scrollarea_h = -scroll_y; 1403 1404 int const mainarea_x = 0; 1405 int const mainarea_y = scroll_y; 1406 int const mainarea_w = Width(screen) * FontWidth(screen); 1407 int const mainarea_h = -scroll_y + Height(screen) * FontHeight(screen); 1408 1409 draw_x_min = refresh_x + refresh_w; 1410 draw_x_max = refresh_x - 1; 1411 draw_y_min = refresh_y + refresh_h; 1412 draw_y_max = refresh_y - 1; 1413 for (jj = 0; jj < active_count; ++jj) { 1414 Graphic *graphic = ordered_graphics[jj]; 1415 int draw_x = graphic->charcol * FontWidth(screen); 1416 int draw_y = graphic->charrow * FontHeight(screen); 1417 int draw_w = graphic->actual_width; 1418 int draw_h = graphic->actual_height; 1419 1420 if (screen->whichBuf != 0) { 1421 if (graphic->bufferid != 0) { 1422 /* clip to alt buffer */ 1423 clip_area(&draw_x, &draw_y, &draw_w, &draw_h, 1424 altarea_x, altarea_y, altarea_w, altarea_h); 1425 } else if (graphic->bufferid == 0) { 1426 /* clip to scrollback area */ 1427 clip_area(&draw_x, &draw_y, &draw_w, &draw_h, 1428 scrollarea_x, scrollarea_y, 1429 scrollarea_w, scrollarea_h); 1430 } 1431 } else { 1432 /* clip to scrollback + normal area */ 1433 clip_area(&draw_x, &draw_y, &draw_w, &draw_h, 1434 mainarea_x, mainarea_y, 1435 mainarea_w, mainarea_h); 1436 } 1437 1438 clip_area(&draw_x, &draw_y, &draw_w, &draw_h, 1439 refresh_x, refresh_y, refresh_w, refresh_h); 1440 1441 TRACE(("refresh: graph=%u\n", jj)); 1442 TRACE((" refresh_x=%d refresh_y=%d refresh_w=%d refresh_h=%d\n", 1443 refresh_x, refresh_y, refresh_w, refresh_h)); 1444 TRACE((" draw_x=%d draw_y=%d draw_w=%d draw_h=%d\n", 1445 draw_x, draw_y, draw_w, draw_h)); 1446 1447 if (draw_w > 0 && draw_h > 0) { 1448 refresh_graphic(screen, graphic, buffer, 1449 refresh_x, refresh_y, 1450 refresh_w, refresh_h, 1451 draw_x, draw_y, 1452 draw_w, draw_h); 1453 if (draw_x < draw_x_min) 1454 draw_x_min = draw_x; 1455 if (draw_x + draw_w - 1 > draw_x_max) 1456 draw_x_max = draw_x + draw_w - 1; 1457 if (draw_y < draw_y_min) 1458 draw_y_min = draw_y; 1459 if (draw_y + draw_h - 1 > draw_y_max) 1460 draw_y_max = draw_y + draw_h - 1; 1461 } 1462 graphic->dirty = 0; 1463 } 1464 } 1465 1466 if (draw_x_max < refresh_x || 1467 draw_x_min > refresh_x + refresh_w - 1 || 1468 draw_y_max < refresh_y || 1469 draw_y_min > refresh_y + refresh_h - 1) { 1470 free(buffer); 1471 return; 1472 } 1473 1474 holes = 0U; 1475 non_holes = 0U; 1476 for (yy = draw_y_min - refresh_y; yy <= draw_y_max - refresh_y; yy++) { 1477 for (xx = draw_x_min - refresh_x; xx <= draw_x_max - refresh_x; xx++) { 1478 const ColorRegister color = buffer[yy * refresh_w + xx]; 1479 if (color.r < 0 || color.g < 0 || color.b < 0) { 1480 holes++; 1481 } else { 1482 non_holes++; 1483 } 1484 } 1485 } 1486 1487 if (non_holes < 1U) { 1488 TRACE(("refresh: visible graphics areas are erased; nothing to do\n")); 1489 free(buffer); 1490 return; 1491 } 1492 1493 /* 1494 * If we have any holes we can't just copy an image rectangle, and masking 1495 * with bitmaps is very expensive. This fallback is surprisingly faster 1496 * than the XPutImage version in some cases, but I don't know why. 1497 * (This is even though there's no X11 primitive for drawing a horizontal 1498 * line of height one and no attempt is made to handle multiple lines at 1499 * once.) 1500 */ 1501 if (holes > 0U) { 1502 GC graphics_gc; 1503 XGCValues xgcv; 1504 ColorRegister last_color; 1505 ColorRegister gc_color; 1506 int run; 1507 1508 memset(&xgcv, 0, sizeof(xgcv)); 1509 xgcv.graphics_exposures = False; 1510 graphics_gc = XCreateGC(display, drawable, GCGraphicsExposures, &xgcv); 1511 if (graphics_gc == None) { 1512 TRACE(("unable to allocate GC for graphics refresh\n")); 1513 free(buffer); 1514 return; 1515 } 1516 1517 last_color.r = -1; 1518 last_color.g = -1; 1519 last_color.b = -1; 1520 gc_color.r = -1; 1521 gc_color.g = -1; 1522 gc_color.b = -1; 1523 run = 0; 1524 for (yy = draw_y_min - refresh_y; yy <= draw_y_max - refresh_y; yy++) { 1525 for (xx = draw_x_min - refresh_x; xx <= draw_x_max - refresh_x; 1526 xx++) { 1527 const ColorRegister color = buffer[yy * refresh_w + xx]; 1528 1529 if (color.r < 0 || color.g < 0 || color.b < 0) { 1530 last_color = color; 1531 if (run > 0) { 1532 XDrawLine(display, drawable, graphics_gc, 1533 OriginX(screen) + refresh_x + xx - run, 1534 (OriginY(screen) - scroll_y) + refresh_y + yy, 1535 OriginX(screen) + refresh_x + xx - 1, 1536 (OriginY(screen) - scroll_y) + refresh_y + yy); 1537 run = 0; 1538 } 1539 continue; 1540 } 1541 1542 if (color.r != last_color.r || 1543 color.g != last_color.g || 1544 color.b != last_color.b) { 1545 last_color = color; 1546 if (run > 0) { 1547 XDrawLine(display, drawable, graphics_gc, 1548 OriginX(screen) + refresh_x + xx - run, 1549 (OriginY(screen) - scroll_y) + refresh_y + yy, 1550 OriginX(screen) + refresh_x + xx - 1, 1551 (OriginY(screen) - scroll_y) + refresh_y + yy); 1552 run = 0; 1553 } 1554 1555 if (color.r != gc_color.r || 1556 color.g != gc_color.g || 1557 color.b != gc_color.b) { 1558 xgcv.foreground = 1559 color_register_to_xpixel(&color, xw); 1560 XChangeGC(display, graphics_gc, GCForeground, &xgcv); 1561 gc_color = color; 1562 } 1563 } 1564 run++; 1565 } 1566 if (run > 0) { 1567 last_color.r = -1; 1568 last_color.g = -1; 1569 last_color.b = -1; 1570 XDrawLine(display, drawable, graphics_gc, 1571 OriginX(screen) + refresh_x + xx - run, 1572 (OriginY(screen) - scroll_y) + refresh_y + yy, 1573 OriginX(screen) + refresh_x + xx - 1, 1574 (OriginY(screen) - scroll_y) + refresh_y + yy); 1575 run = 0; 1576 } 1577 } 1578 1579 XFreeGC(display, graphics_gc); 1580 } else { 1581 XGCValues xgcv; 1582 GC graphics_gc; 1583 ColorRegister old_color; 1584 Pixel fg; 1585 XImage *image; 1586 char *imgdata; 1587 unsigned image_w, image_h; 1588 1589 memset(&xgcv, 0, sizeof(xgcv)); 1590 xgcv.graphics_exposures = False; 1591 graphics_gc = XCreateGC(display, drawable, GCGraphicsExposures, &xgcv); 1592 if (graphics_gc == None) { 1593 TRACE(("unable to allocate GC for graphics refresh\n")); 1594 free(buffer); 1595 return; 1596 } 1597 1598 /* FIXME: is it worth reusing the GC/Image/imagedata across calls? */ 1599 /* FIXME: is it worth using shared memory when available? */ 1600 image_w = (unsigned) draw_x_max + 1U - (unsigned) draw_x_min; 1601 image_h = (unsigned) draw_y_max + 1U - (unsigned) draw_y_min; 1602 image = XCreateImage(display, xw->visInfo->visual, 1603 (unsigned) xw->visInfo->depth, 1604 ZPixmap, 0, NULL, 1605 image_w, image_h, 1606 sizeof(int) * 8U, 0); 1607 if (!image) { 1608 TRACE(("unable to allocate XImage for graphics refresh\n")); 1609 XFreeGC(display, graphics_gc); 1610 free(buffer); 1611 return; 1612 } 1613 imgdata = malloc(image_h * (unsigned) image->bytes_per_line); 1614 if (!imgdata) { 1615 TRACE(("unable to allocate XImage for graphics refresh\n")); 1616 XDestroyImage(image); 1617 XFreeGC(display, graphics_gc); 1618 free(buffer); 1619 return; 1620 } 1621 image->data = imgdata; 1622 1623 fg = 0U; 1624 old_color.r = -1; 1625 old_color.g = -1; 1626 old_color.b = -1; 1627 for (yy = draw_y_min - refresh_y; yy <= draw_y_max - refresh_y; yy++) { 1628 for (xx = draw_x_min - refresh_x; xx <= draw_x_max - refresh_x; 1629 xx++) { 1630 const ColorRegister color = buffer[yy * refresh_w + xx]; 1631 1632 if (color.r != old_color.r || 1633 color.g != old_color.g || 1634 color.b != old_color.b) { 1635 fg = color_register_to_xpixel(&color, xw); 1636 old_color = color; 1637 } 1638 1639 XPutPixel(image, xx + refresh_x - draw_x_min, 1640 yy + refresh_y - draw_y_min, fg); 1641 } 1642 } 1643 1644 XPutImage(display, drawable, graphics_gc, image, 1645 0, 0, 1646 OriginX(screen) + draw_x_min, 1647 (OriginY(screen) - scroll_y) + draw_y_min, 1648 image_w, image_h); 1649 free(imgdata); 1650 image->data = NULL; 1651 XDestroyImage(image); 1652 XFreeGC(display, graphics_gc); 1653 } 1654 1655 free(buffer); 1656 XFlush(display); 1657} 1658 1659void 1660refresh_displayed_graphics(XtermWidget xw, 1661 int leftcol, 1662 int toprow, 1663 int ncols, 1664 int nrows) 1665{ 1666 refresh_graphics(xw, leftcol, toprow, ncols, nrows, 0); 1667} 1668 1669void 1670refresh_modified_displayed_graphics(XtermWidget xw) 1671{ 1672 TScreen const *screen = TScreenOf(xw); 1673 refresh_graphics(xw, 0, 0, MaxCols(screen), MaxRows(screen), 1); 1674} 1675 1676void 1677scroll_displayed_graphics(XtermWidget xw, int rows) 1678{ 1679 TScreen const *screen = TScreenOf(xw); 1680 Graphic *graphic; 1681 unsigned ii; 1682 1683 TRACE(("graphics scroll: moving all up %d rows\n", rows)); 1684 /* FIXME: VT125 ReGIS graphics are fixed at the upper left of the display; need to verify */ 1685 1686 FOR_EACH_SLOT(ii) { 1687 if (!(graphic = getActiveSlot(ii))) 1688 continue; 1689 if (graphic->bufferid != screen->whichBuf) 1690 continue; 1691 1692 graphic->charrow -= rows; 1693 } 1694} 1695 1696void 1697pixelarea_clear_displayed_graphics(TScreen const *screen, 1698 int winx, 1699 int winy, 1700 int w, 1701 int h) 1702{ 1703 unsigned ii; 1704 1705 FOR_EACH_SLOT(ii) { 1706 Graphic *graphic; 1707 /* FIXME: are these coordinates (scrolled) screen-relative? */ 1708 int const scroll_y = (screen->whichBuf == 0 1709 ? screen->topline * FontHeight(screen) 1710 : 0); 1711 int graph_x; 1712 int graph_y; 1713 int x, y; 1714 1715 if (!(graphic = getActiveSlot(ii))) 1716 continue; 1717 if (graphic->bufferid != screen->whichBuf) 1718 continue; 1719 1720 graph_x = graphic->charcol * FontWidth(screen); 1721 graph_y = graphic->charrow * FontHeight(screen); 1722 x = winx - graph_x; 1723 y = (winy - scroll_y) - graph_y; 1724 1725 TRACE(("pixelarea clear graphics: screen->topline=%d winx=%d winy=%d w=%d h=%d x=%d y=%d\n", 1726 screen->topline, 1727 winx, winy, 1728 w, h, 1729 x, y)); 1730 erase_graphic(graphic, x, y, w, h); 1731 } 1732} 1733 1734void 1735chararea_clear_displayed_graphics(TScreen const *screen, 1736 int leftcol, 1737 int toprow, 1738 int ncols, 1739 int nrows) 1740{ 1741 int const x = leftcol * FontWidth(screen); 1742 int const y = toprow * FontHeight(screen); 1743 int const w = ncols * FontWidth(screen); 1744 int const h = nrows * FontHeight(screen); 1745 1746 TRACE(("chararea clear graphics: screen->topline=%d leftcol=%d toprow=%d nrows=%d ncols=%d x=%d y=%d w=%d h=%d\n", 1747 screen->topline, 1748 leftcol, toprow, 1749 nrows, ncols, 1750 x, y, w, h)); 1751 pixelarea_clear_displayed_graphics(screen, x, y, w, h); 1752} 1753 1754void 1755reset_displayed_graphics(TScreen const *screen) 1756{ 1757 unsigned ii; 1758 1759 init_color_registers(getSharedRegisters(), screen->terminal_id); 1760 1761 TRACE(("resetting all graphics\n")); 1762 FOR_EACH_SLOT(ii) { 1763 deactivateSlot(ii); 1764 } 1765} 1766 1767#ifdef NO_LEAKS 1768void 1769noleaks_graphics(void) 1770{ 1771 unsigned ii; 1772 1773 FOR_EACH_SLOT(ii) { 1774 deactivateSlot(ii); 1775 } 1776} 1777#endif 1778