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