graphics_regis.c revision ad37e533
1/* $XTermId: graphics_regis.c,v 1.128 2021/02/25 23:17:48 tom Exp $ */ 2 3/* 4 * Copyright 2014-2020,2021 by Ross Combs 5 * Copyright 2014-2020,2021 by Thomas E. Dickey 6 * 7 * All Rights Reserved 8 * 9 * Permission is hereby granted, free of charge, to any person obtaining a 10 * copy of this software and associated documentation files (the 11 * "Software"), to deal in the Software without restriction, including 12 * without limitation the rights to use, copy, modify, merge, publish, 13 * distribute, sublicense, and/or sell copies of the Software, and to 14 * permit persons to whom the Software is furnished to do so, subject to 15 * the following conditions: 16 * 17 * The above copyright notice and this permission notice shall be included 18 * in all copies or substantial portions of the Software. 19 * 20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 21 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 22 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 23 * IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY 24 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 25 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 26 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 27 * 28 * Except as contained in this notice, the name(s) of the above copyright 29 * holders shall not be used in advertising or otherwise to promote the 30 * sale, use or other dealings in this Software without prior written 31 * authorization. 32 */ 33 34#include <xterm.h> 35 36#include <stdio.h> 37#include <ctype.h> 38#include <math.h> 39#include <stdlib.h> 40 41#include <fontutils.h> 42#include <ptyx.h> 43 44#include <assert.h> 45#include <graphics.h> 46#include <graphics_regis.h> 47 48/* get rid of shadowing warnings (we will not draw Bessel functions) */ 49#define y1 my_y1 50#define y0 my_y0 51 52#define SCALE_FIXED_POINT 16U 53 54#undef DEBUG_PARSING 55#undef DEBUG_ALPHABET_LOOKUP 56#undef DEBUG_ALPHABETS 57#undef DEBUG_BEZIER 58#undef DEBUG_SPLINE_SEGMENTS 59#undef DEBUG_SPLINE_POINTS 60#undef DEBUG_SPLINE_WITH_ROTATION 61#undef DEBUG_SPLINE_WITH_OVERDRAW 62#undef DEBUG_ARC_POINTS 63#undef DEBUG_ARC_CENTER 64#undef DEBUG_ARC_START 65#undef DEBUG_ARC_END 66#undef DEBUG_SPECIFIC_CHAR_METRICS 67#define IS_DEBUG_CHAR(CH) ((CH) == 'W') /* glyphs to dump to terminal */ 68#undef DEBUG_COMPUTED_FONT_METRICS 69#undef DEBUG_FONT_NAME 70#undef DEBUG_FONT_SIZE_SEARCH 71#undef DEBUG_XFT_GLYPH 72#undef DEBUG_GLYPH_RETRIEVAL 73#undef DEBUG_XFT_GLYPH_LOADING 74#undef DEBUG_LOAD 75 76/* controls for extensions over VT3x0 limitations */ 77#define ENABLE_RGB_COLORSPECS 78#undef ENABLE_FREE_ROTATION 79#undef ENABLE_DISTORTIONLESS_ROTATION 80#define ENABLE_UPLOAD_ALPHABET_FROM_FONT 81#define ENABLE_UPLOAD_ALPHABET_ZERO 82#define ENABLE_USER_FONT_SIZE 83#define ENABLE_VARIABLE_ITALICS 84 85#define MIN_ITERATIONS_BEFORE_REFRESH 10U 86#define MIN_MS_BEFORE_REFRESH 33 87/* *INDENT-OFF* */ 88typedef struct RegisPoint { 89 int x, y; 90} RegisPoint; 91 92typedef struct RegisWriteControls { 93 unsigned pv_multiplier; 94 unsigned pattern; 95 unsigned pattern_multiplier; 96 unsigned invert_pattern; 97 unsigned plane_mask; 98 unsigned write_style; 99 RegisterNum foreground; 100 unsigned shading_enabled; 101 char shading_character; 102 int shading_reference; 103 unsigned shading_reference_dim; 104 unsigned line_width; 105} RegisWriteControls; 106 107typedef struct RegisTextControls { 108 unsigned alphabet_num; 109 unsigned character_set_l; /* default: "(B" (ASCII) */ 110 unsigned character_set_r; /* default: "-@" (Latin-1) */ 111 unsigned character_display_w; 112 unsigned character_display_h; 113 unsigned character_unit_cell_w; 114 unsigned character_unit_cell_h; 115 int character_inc_x; 116 int character_inc_y; 117 int string_rotation; 118 int character_rotation; 119 int slant; /* for italic/oblique */ 120} RegisTextControls; 121 122#define FixedCopy(dst, src, len) strncpy(dst, src, len - 1)[len - 1] = '\0' 123#define CopyFontname(dst, src) FixedCopy(dst, src, (size_t) REGIS_FONTNAME_LEN) 124 125#define MAX_REGIS_PAGES 8U 126 127#define MAX_REGIS_ALPHABETS 8U 128#define REGIS_ALPHABET_NAME_LEN 11U 129#define REGIS_FONTNAME_LEN 256U 130/* enough for a 16x24 font (about 100KB) */ 131#define MAX_REGIS_ALPHABET_BYTES (256U * 16U * 24U) 132#define MAX_GLYPH_PIXELS 8192U 133#define MAX_GLYPHS 256U 134#define INVALID_ALPHABET_NUM ~0U 135 136typedef struct RegisAlphabet { 137 unsigned alphabet_num; 138 unsigned pixw, pixh; 139 char name[REGIS_ALPHABET_NAME_LEN]; 140 char fontname[REGIS_FONTNAME_LEN]; 141 int use_font; 142 int loaded[MAX_GLYPHS]; 143 Char *bytes; 144} RegisAlphabet; 145 146typedef struct RegisDataFragment { 147 char const *start; 148 unsigned pos; 149 unsigned len; 150} RegisDataFragment; 151/* *INDENT-ON* */ 152 153#define POSITION_STACK_SIZE 16U 154#define DUMMY_STACK_X -32768 155#define DUMMY_STACK_Y -32768 156 157#define CURVE_POSITION_ARC_EDGE 0U 158#define CURVE_POSITION_ARC_CENTER 1U 159#define CURVE_POSITION_OPEN_CURVE 2U 160#define CURVE_POSITION_CLOSED_CURVE 3U 161 162#define MAX_INPUT_CURVE_POINTS 16U 163#define MAX_CURVE_POINTS (MAX_INPUT_CURVE_POINTS + 4U) 164 165#define MAX_FILL_POINTS 2048U 166 167typedef struct RegisParseState { 168 RegisDataFragment input; 169 char *temp; 170 unsigned templen; 171 char command; 172 char option; 173 /* position stack */ 174 int stack_x[POSITION_STACK_SIZE]; 175 int stack_y[POSITION_STACK_SIZE]; 176 unsigned stack_next; /* next empty position */ 177 /* curve options */ 178 int curve_mode; 179 int arclen; 180 int x_points[MAX_CURVE_POINTS]; 181 int y_points[MAX_CURVE_POINTS]; 182 unsigned num_points; 183 /* load options */ 184 char load_name[REGIS_ALPHABET_NAME_LEN]; 185 unsigned load_alphabet; 186 unsigned load_w, load_h; 187 unsigned load_index; 188 unsigned load_glyph; 189 unsigned load_row; 190 /* text options */ 191 unsigned text_tilt_state; 192} RegisParseState; 193 194#define TEXT_TILT_STATE_READY 0U 195#define TEXT_TILT_STATE_GOT_D 1U 196#define TEXT_TILT_STATE_GOT_DS 2U 197#define TEXT_TILT_STATE_GOT_DSD 3U 198 199typedef struct RegisGraphicsContext { 200 XtermWidget current_widget; 201 Graphic *destination_graphic; 202 Graphic *display_graphic; 203 int graphics_termid; 204 int x_off, y_off; 205 int x_div, y_div; 206 int width, height; 207 unsigned all_planes; 208 RegisterNum background; 209 char const *builtin_font; 210 RegisAlphabet alphabets[MAX_REGIS_ALPHABETS]; 211 RegisWriteControls persistent_write_controls; 212 RegisWriteControls temporary_write_controls; 213 RegisTextControls persistent_text_controls; 214 RegisTextControls temporary_text_controls; 215 RegisTextControls *current_text_controls; 216 int multi_input_mode; 217 int graphics_output_cursor_x; 218 int graphics_output_cursor_y; 219 unsigned pattern_count; 220 unsigned pattern_bit; 221 int fill_mode; 222 RegisPoint fill_points[MAX_FILL_POINTS]; 223 unsigned fill_point_count; 224 unsigned destination_page; 225 unsigned display_page; 226 int force_refresh; 227} RegisGraphicsContext; 228 229static RegisGraphicsContext persistent_context; 230static RegisParseState persistent_state; 231 232#define MAX_PATTERN_BITS 8U 233 234#define WRITE_STYLE_OVERLAY 1U 235#define WRITE_STYLE_REPLACE 2U 236#define WRITE_STYLE_COMPLEMENT 3U 237#define WRITE_STYLE_ERASE 4U 238 239#define WRITE_SHADING_REF_Y 0U 240#define WRITE_SHADING_REF_X 1U 241#define WRITE_SHADING_REF_NONE 2U 242 243/* keypress event example: http://iraf.net/forum/viewtopic.php?showtopic=61692 */ 244 245#define MIN2(X, Y) ( (X) < (Y) ? (X) : (Y) ) 246#define MIN3(X, Y, Z) ( MIN2(MIN2((X), (Y)), MIN2((Y), (Z))) ) 247#define MAX2(X, Y) ( (X) > (Y) ? (X) : (Y) ) 248#define MAX3(X, Y, Z) ( MAX2(MAX2((X), (Y)), MAX2((Y), (Z))) ) 249 250#define ROT_LEFT_N(V, N) ( (((V) << ((N) & 3U )) & 255U) | \ 251 ((V) >> (8U - ((N) & 3U))) ) 252#define ROT_LEFT(V) ( (((V) << 1U) & 255U) | ((V) >> 7U) ) 253 254/* convert user coordinates to absolute pixel coordinates */ 255#define SCALE_XCOORD(C, X, S) ( ( (X) * ((C)->width - 1) ) / ( (C)->x_div * (S) ) ) 256#define SCALE_YCOORD(C, Y, S) ( ( (Y) * ((C)->height - 1) ) / ( (C)->y_div * (S) ) ) 257#define TRANSLATE_XCOORD(C, X, S) SCALE_XCOORD((C), (X) - (C)->x_off * (S), (S) ) 258#define TRANSLATE_YCOORD(C, Y, S) SCALE_YCOORD((C), (Y) - (C)->y_off * (S), (S) ) 259 260#if 0 261/* convert absolute pixel coordinate to user coordinates */ 262#define SCALE_XPIX(C, X, S) ( ( (X) * ((C)->x_div * (S) ) ) / ((C)->width - 1) ) 263#define SCALE_YPIX(C, Y, S) ( ( (Y) * ((C)->y_div * (S) ) ) / ((C)->height - 1) ) 264#define TRANSLATE_XPIX(C, X, S) ( SCALE_XPIX((C), (X), (S) ) + (C)->x_off * (S) ) 265#define TRANSLATE_YPIX(C, Y, S) ( SCALE_YPIX((C), (Y), (S) ) + (C)->y_off * (S) ) 266#endif 267 268#define READ_PIXEL(C, X, Y) read_pixel((C)->destination_graphic, (X), (Y)) 269#define DRAW_PIXEL(C, X, Y, COL) draw_solid_pixel((C)->destination_graphic, (X), (Y), (COL)) 270#define DRAW_ALL(C, COL) \ 271 draw_solid_rectangle((C)->destination_graphic, 0, 0, (C)->width, (C)->height, (COL)) 272 273static unsigned get_shade_character_pixel(Char const *pixels, 274 unsigned w, unsigned h, 275 unsigned smaxf, unsigned scale, 276 int slant_dx, int px, int py); 277static void get_bitmap_of_character(RegisGraphicsContext const *context, 278 int ch, unsigned maxw, unsigned maxh, 279 Char *pixels, 280 unsigned *w, unsigned *h, 281 unsigned max_pixels); 282 283static void 284init_regis_load_state(RegisParseState *state) 285{ 286 state->load_index = MAX_REGIS_ALPHABETS; 287 state->load_w = 8U; 288 state->load_h = 10U; 289 state->load_alphabet = 1U; /* FIXME: is this the correct default */ 290 state->load_name[0] = '\0'; 291 state->load_glyph = (unsigned) (Char) '\0'; 292 state->load_row = 0U; 293} 294 295static void 296init_regis_parse_state(RegisParseState *state) 297{ 298 state->command = '_'; 299 state->option = '_'; 300 state->stack_next = 0U; 301 state->load_index = MAX_REGIS_ALPHABETS; 302 init_regis_load_state(state); 303} 304 305static int 306ifloor(double d) 307{ 308 double dl = floor(d); 309 return (int) dl; 310} 311 312static int 313isqrt(double d) 314{ 315 double dl = sqrt(d); 316 return (int) dl; 317} 318 319static void 320draw_regis_pixel(RegisGraphicsContext *context, int x, int y, 321 unsigned value) 322{ 323 unsigned color = 0; 324 325 switch (context->temporary_write_controls.write_style) { 326 case WRITE_STYLE_OVERLAY: 327 /* 328 * Update pixels with foreground when pattern is 1, 329 * don't change when pattern is 0. 330 */ 331 if (!value) { 332 return; 333 } 334 335 if (context->temporary_write_controls.invert_pattern) { 336 color = context->background; 337 } else { 338 color = context->temporary_write_controls.foreground; 339 } 340 break; 341 342 case WRITE_STYLE_REPLACE: 343 /* 344 * Update pixels with foreground when pattern is 1, 345 * set to background when pattern is 0. 346 */ 347 { 348 unsigned fg, bg; 349 350 if (context->temporary_write_controls.invert_pattern) { 351 fg = context->background; 352 bg = context->temporary_write_controls.foreground; 353 } else { 354 fg = context->temporary_write_controls.foreground; 355 bg = context->background; 356 } 357 color = value ? fg : bg; 358 } 359 break; 360 361 case WRITE_STYLE_COMPLEMENT: 362 /* 363 * Update pixels with background when pattern is 1, 364 * don't change when pattern is 0. 365 */ 366 if (!value) { 367 return; 368 } 369 370 color = READ_PIXEL(context, x, y); 371 if (color == COLOR_HOLE) 372 color = context->background; 373 color = color ^ context->all_planes; 374 break; 375 376 case WRITE_STYLE_ERASE: 377 /* Update pixels to foreground. */ 378 if (context->temporary_write_controls.invert_pattern) { 379 color = context->temporary_write_controls.foreground; 380 } else { 381 color = context->background; 382 } 383 break; 384 } 385 386 if (context->temporary_write_controls.plane_mask != context->all_planes) { 387 unsigned old_color = READ_PIXEL(context, x, y); 388 if (old_color == COLOR_HOLE) 389 old_color = context->background; 390 color = (color & context->temporary_write_controls.plane_mask) | 391 (old_color & ~context->temporary_write_controls.plane_mask); 392 } 393 394 DRAW_PIXEL(context, x, y, color); 395} 396 397static void 398shade_pattern_to_pixel(RegisGraphicsContext *context, unsigned dim, int ref, 399 int x, int y) 400{ 401 unsigned value; 402 403 if (dim == WRITE_SHADING_REF_X) { 404 int delta = x > ref ? 1 : -1; 405 int curr_x; 406 407 context->pattern_bit = 1U << (((unsigned) y) & 7U); 408 for (curr_x = ref; curr_x != x + delta; curr_x += delta) { 409 value = context->temporary_write_controls.pattern & 410 context->pattern_bit; 411 draw_regis_pixel(context, curr_x, y, value); 412 } 413 } else if (dim == WRITE_SHADING_REF_Y) { 414 int delta = y > ref ? 1 : -1; 415 int curr_y; 416 417 for (curr_y = ref; curr_y != y + delta; curr_y += delta) { 418 context->pattern_bit = 1U << (((unsigned) curr_y) & 7U); 419 value = context->temporary_write_controls.pattern & 420 context->pattern_bit; 421 draw_regis_pixel(context, x, curr_y, value); 422 } 423 } else { 424 TRACE(("ERROR: shading requested, but there is no reference axis\n")); 425 } 426} 427 428static void 429shade_char_to_pixel(RegisGraphicsContext *context, Char const *pixels, 430 unsigned w, unsigned h, unsigned dim, int ref, int x, int y) 431{ 432 unsigned xmaxf = context->current_text_controls->character_unit_cell_w; 433 unsigned ymaxf = context->current_text_controls->character_unit_cell_h; 434 unsigned smaxf; 435 unsigned s; 436 unsigned scale; 437 unsigned value; 438 439 if (xmaxf > ymaxf) { 440 smaxf = ymaxf; 441 s = h; 442 } else { 443 smaxf = xmaxf; 444 s = w; 445 } 446 scale = (s << SCALE_FIXED_POINT) / smaxf; 447 448 if (dim == WRITE_SHADING_REF_X) { 449 int delta = x > ref ? 1 : -1; 450 int curr_x; 451 452 for (curr_x = ref; curr_x != x + delta; curr_x += delta) { 453 value = get_shade_character_pixel(pixels, w, h, smaxf, scale, 0, 454 curr_x, y); 455 draw_regis_pixel(context, curr_x, y, value); 456 } 457 } else if (dim == WRITE_SHADING_REF_Y) { 458 int delta = y > ref ? 1 : -1; 459 int curr_y; 460 461 for (curr_y = ref; curr_y != y + delta; curr_y += delta) { 462 value = get_shade_character_pixel(pixels, w, h, smaxf, scale, 0, x, 463 curr_y); 464 draw_regis_pixel(context, x, curr_y, value); 465 } 466 } else { 467 TRACE(("ERROR: shading requested, but there is no reference axis\n")); 468 } 469} 470 471static void 472draw_patterned_pixel(RegisGraphicsContext *context, int x, int y) 473{ 474 if (context->pattern_count >= 475 context->temporary_write_controls.pattern_multiplier) { 476 context->pattern_count = 0U; 477 context->pattern_bit = ROT_LEFT(context->pattern_bit); 478 } 479 context->pattern_count++; 480 481 draw_regis_pixel(context, x, y, 482 context->temporary_write_controls.pattern & 483 context->pattern_bit); 484} 485 486static void 487shade_to_pixel(RegisGraphicsContext *context, unsigned dim, int ref, 488 int x, int y) 489{ 490 if (context->temporary_write_controls.shading_character != '\0') { 491 unsigned xmaxf = context->current_text_controls->character_unit_cell_w; 492 unsigned ymaxf = context->current_text_controls->character_unit_cell_h; 493 char ch = context->temporary_write_controls.shading_character; 494 Char pixels[MAX_GLYPH_PIXELS]; 495 unsigned w, h; 496 497 get_bitmap_of_character(context, ch, xmaxf, ymaxf, pixels, &w, &h, 498 MAX_GLYPH_PIXELS); 499 if (w > 0 && h > 0) { 500 shade_char_to_pixel(context, pixels, w, h, dim, ref, x, y); 501 } 502 } else { 503 shade_pattern_to_pixel(context, dim, ref, x, y); 504 } 505} 506 507static void 508draw_or_save_patterned_pixel(RegisGraphicsContext *context, int x, int y) 509{ 510 if (context->fill_mode == 1) { 511 if (context->fill_point_count >= MAX_FILL_POINTS) { 512 TRACE(("point %d,%d can not be added to filled polygon\n", 513 x, y)); 514 return; 515 } 516 if (context->fill_point_count > 0U && 517 context->fill_points[context->fill_point_count - 1U].x == x && 518 context->fill_points[context->fill_point_count - 1U].y == y) { 519 return; 520 } 521 context->fill_points[context->fill_point_count].x = x; 522 context->fill_points[context->fill_point_count].y = y; 523 context->fill_point_count++; 524 return; 525 } 526 527 if (context->temporary_write_controls.shading_enabled) { 528 unsigned dim = context->temporary_write_controls.shading_reference_dim; 529 int ref = context->temporary_write_controls.shading_reference; 530 531 shade_to_pixel(context, dim, ref, x, y); 532 return; 533 } 534 535 draw_patterned_pixel(context, x, y); 536} 537 538static int 539sort_points(void const *l, void const *r) 540{ 541 RegisPoint const *const lp = l; 542 RegisPoint const *const rp = r; 543 544 if (lp->y < rp->y) 545 return -1; 546 if (lp->y > rp->y) 547 return +1; 548 if (lp->x < rp->x) 549 return -1; 550 if (lp->x > rp->x) 551 return +1; 552 return 0; 553} 554 555static void 556draw_shaded_polygon(RegisGraphicsContext *context) 557{ 558 unsigned p; 559 int old_x, old_y; 560 int inside; 561 Char pixels[MAX_GLYPH_PIXELS]; 562 unsigned w = 1, h = 1; 563 564 char ch = context->temporary_write_controls.shading_character; 565 unsigned xmaxf = context->current_text_controls->character_unit_cell_w; 566 unsigned ymaxf = context->current_text_controls->character_unit_cell_h; 567 568 get_bitmap_of_character(context, ch, xmaxf, ymaxf, pixels, &w, &h, 569 MAX_GLYPH_PIXELS); 570 if (w < 1U || h < 1U) { 571 return; 572 } 573 574 qsort(context->fill_points, (size_t) context->fill_point_count, 575 sizeof(context->fill_points[0]), sort_points); 576 577 old_x = DUMMY_STACK_X; 578 old_y = DUMMY_STACK_Y; 579 inside = 0; 580 for (p = 0U; p < context->fill_point_count; p++) { 581 int new_x = context->fill_points[p].x; 582 int new_y = context->fill_points[p].y; 583#if 0 584 printf("got %d,%d (%d,%d) inside=%d\n", new_x, new_y, old_x, old_y, inside); 585#endif 586 587 /* 588 * FIXME: This is using pixels to represent lines which loses 589 * information about exact slope and how many lines are present which 590 * causes misbehavior with some inputs (especially complex polygons). 591 * It also takes more room than remembering vertices, but I'd rather 592 * not have to implement line segments for arcs. Maybe store a count 593 * at each vertex instead (doesn't fix the slope problem). 594 */ 595 /* 596 * FIXME: Change this to only draw inside of polygons, and round 597 * points in a uniform direction to avoid overlapping drawing. As an 598 * option we could continue to support drawing the outline. 599 */ 600 if (new_y != old_y) { 601 if (inside) { 602 /* 603 * Just draw the vertical line when there is not a matching 604 * edge on the right side. 605 */ 606 shade_char_to_pixel(context, pixels, w, h, 607 WRITE_SHADING_REF_X, 608 old_x, old_x, old_y); 609 } 610 inside = 1; 611 } else { 612 if (inside) { 613 shade_char_to_pixel(context, pixels, w, h, 614 WRITE_SHADING_REF_X, 615 old_x, new_x, new_y); 616 } 617 if (new_x > old_x + 1) { 618 inside = !inside; 619 } 620 } 621 622 old_x = new_x; 623 old_y = new_y; 624 } 625 626 context->destination_graphic->dirty = 1; 627} 628 629static void 630draw_filled_polygon(RegisGraphicsContext *context) 631{ 632 unsigned p; 633 int old_x, old_y; 634 int inside; 635 636 qsort(context->fill_points, (size_t) context->fill_point_count, 637 sizeof(context->fill_points[0]), sort_points); 638 639 old_x = DUMMY_STACK_X; 640 old_y = DUMMY_STACK_Y; 641 inside = 0; 642 for (p = 0U; p < context->fill_point_count; p++) { 643 int new_x = context->fill_points[p].x; 644 int new_y = context->fill_points[p].y; 645#if 0 646 printf("got %d,%d (%d,%d) inside=%d\n", new_x, new_y, old_x, old_y, inside); 647#endif 648 649 /* 650 * FIXME: This is using pixels to represent lines which loses 651 * information about exact slope and how many lines are present which 652 * causes misbehavior with some inputs (especially complex polygons). 653 * It also takes more room than remembering vertices, but I'd rather 654 * not have to implement line segments for arcs. Maybe store a count 655 * at each vertex instead (doesn't fix the slope problem). 656 */ 657 /* 658 * FIXME: Change this to only draw inside of polygons, and round 659 * points in a uniform direction to avoid overlapping drawing. As an 660 * option we could continue to support drawing the outline. 661 */ 662 if (new_y != old_y) { 663 if (inside) { 664 /* 665 * Just draw the vertical line when there is not a matching 666 * edge on the right side. 667 */ 668 shade_pattern_to_pixel(context, WRITE_SHADING_REF_X, 669 old_x, old_x, old_y); 670 } 671 inside = 1; 672 } else { 673 if (inside) { 674 shade_pattern_to_pixel(context, WRITE_SHADING_REF_X, 675 old_x, new_x, new_y); 676 } 677 if (new_x > old_x + 1) { 678 inside = !inside; 679 } 680 } 681 682 old_x = new_x; 683 old_y = new_y; 684 } 685 686 context->destination_graphic->dirty = 1; 687} 688 689static void 690draw_patterned_line(RegisGraphicsContext *context, int x1, int y1, 691 int x2, int y2) 692{ 693 int x, y; 694 int dx, dy; 695 int dir, diff; 696 697 dx = abs(x1 - x2); 698 dy = abs(y1 - y2); 699 700 if (dx > dy) { 701 if (x1 > x2) { 702 int tmp; 703 EXCHANGE(x1, x2, tmp); 704 EXCHANGE(y1, y2, tmp); 705 } 706 if (y1 < y2) 707 dir = 1; 708 else if (y1 > y2) 709 dir = -1; 710 else 711 dir = 0; 712 713 diff = 0; 714 y = y1; 715 for (x = x1; x <= x2; x++) { 716 if (diff >= dx) { 717 diff -= dx; 718 y += dir; 719 } 720 diff += dy; 721 draw_or_save_patterned_pixel(context, x, y); 722 } 723 } else { 724 if (y1 > y2) { 725 int tmp; 726 EXCHANGE(y1, y2, tmp); 727 EXCHANGE(x1, x2, tmp); 728 } 729 if (x1 < x2) 730 dir = 1; 731 else if (x1 > x2) 732 dir = -1; 733 else 734 dir = 0; 735 736 diff = 0; 737 x = x1; 738 for (y = y1; y <= y2; y++) { 739 if (diff >= dy) { 740 diff -= dy; 741 x += dir; 742 } 743 diff += dx; 744 draw_or_save_patterned_pixel(context, x, y); 745 } 746 } 747 748 context->destination_graphic->dirty = 1; 749} 750 751typedef struct { 752 int dxx; 753 int dxy; 754 int dyx; 755 int dyy; 756} quadmap_coords; 757 758static void 759draw_patterned_arc(RegisGraphicsContext *context, 760 int cx, int cy, 761 int ex, int ey, 762 int a_start, int a_length, 763 int *ex_final, int *ey_final) 764{ 765 const double third = hypot((double) (cx - ex), (double) (cy - ey)); 766 const int radius = (int) third; 767 const int ra = radius; 768 const int rb = radius; 769 const quadmap_coords neg_quadmap[4] = 770 { 771 {-1, 0, 0, +1}, 772 {0, -1, -1, 0}, 773 {+1, 0, 0, -1}, 774 {0, +1, +1, 0}, 775 }; 776 const quadmap_coords pos_quadmap[4] = 777 { 778 {-1, 0, 0, -1}, 779 {0, -1, +1, 0}, 780 {+1, 0, 0, +1}, 781 {0, +1, -1, 0}, 782 }; 783 const quadmap_coords *quadmap; 784 int total_points; 785 int half_degree; 786 int points_start, points_stop; 787 int points; 788 unsigned iterations; 789 long rx, ry; 790 long dx, dy; 791 int x, y; 792 long e2; 793 long error; 794 795 TRACE(("orig a_length=%d a_start=%d\n", a_length, a_start)); 796 if (a_length == 0) 797 return; 798 if (a_length > 0) { 799 quadmap = pos_quadmap; 800 } else { 801 quadmap = neg_quadmap; 802 if (a_start != 0) 803 a_start = 3600 - a_start; 804 a_length = abs(a_length); 805 } 806 TRACE(("positive a_length=%d a_start=%d\n", a_length, a_start)); 807 808 rx = -ra; 809 ry = 0; 810 e2 = rb; 811 dx = (2 * rx + 1) * e2 * e2; 812 dy = rx * rx; 813 error = dx + dy; 814 total_points = 0; 815 do { 816 total_points += 4; 817 e2 = 2 * error; 818 if (e2 >= dx) { 819 rx++; 820 dx += 2 * rb * rb; 821 error += dx; 822 } 823 if (e2 <= dy) { 824 ry++; 825 dy += 2 * ra * ra; 826 error += dy; 827 } 828 } 829 while (rx <= 0); 830 831 /* FIXME: This is apparently not accurate enough because some arcs start or 832 * end a few pixels off. Maybe compare line slopes in the loop below 833 * instead? 834 */ 835 half_degree = total_points * 5; 836 points_start = (total_points * a_start - half_degree) / 3600; 837 points_stop = (total_points * a_start + 838 total_points * a_length + half_degree) / 3600; 839 TRACE(("drawing arc with %d points clockwise from %g degrees for %g degrees (from point %d to %d out of %d)\n", 840 total_points, a_start / 10.0, a_length / 10.0, points_start, points_stop, 841 total_points)); 842 843 /* FIXME: The four pixels at the cardinal directions are double-drawn. */ 844 points = 0; 845 for (iterations = 0U; iterations < 8U; iterations++) { 846 int q2 = iterations & 0x3; 847 848 rx = -ra; 849 ry = 0; 850 e2 = rb; 851 dx = (2 * rx + 1) * e2 * e2; 852 dy = rx * rx; 853 error = dx + dy; 854 do { 855#ifdef DEBUG_ARC_POINTS 856 double rad = atan2( 857 (double) (quadmap[q2].dyx * rx + 858 quadmap[q2].dyy * ry), 859 (double) (quadmap[q2].dxx * rx + 860 quadmap[q2].dxy * ry)); 861 double deg = (360.0 * rad / (2.0 * M_PI)); 862 if (deg < 0.0) 863 deg += 360.0; 864#endif 865 866 if (points >= points_start && points <= points_stop) { 867 x = (int) (cx + 868 quadmap[q2].dxx * rx + 869 quadmap[q2].dxy * ry); 870 y = (int) (cy + 871 quadmap[q2].dyx * rx + 872 quadmap[q2].dyy * ry); 873#ifdef DEBUG_ARC_POINTS 874 TRACE(("drawing point %u at %d,%d (%.5g deg)\n", 875 points, x, y, deg)); 876#endif 877 draw_or_save_patterned_pixel(context, x, y); 878 if (ex_final) 879 *ex_final = x; 880 if (ey_final) 881 *ey_final = y; 882 } else { 883#ifdef DEBUG_ARC_POINTS 884 x = (int) (cx + quadmap[q2].dxx * rx + quadmap[q2].dxy * ry); 885 y = (int) (cy + quadmap[q2].dyx * rx + quadmap[q2].dyy * ry); 886 TRACE(("skipping point %u at %d,%d which is outside of range (%.5g deg)\n", 887 points, x, y, deg)); 888#endif 889 } 890 points++; 891 892 e2 = 2 * error; 893 if (e2 >= dx) { 894 rx++; 895 dx += 2 * rb * rb; 896 error += dx; 897 } 898 if (e2 <= dy) { 899 ry++; 900 dy += 2 * ra * ra; 901 error += dy; 902 } 903 } 904 while (rx <= 0); 905 } 906 907 context->destination_graphic->dirty = 1; 908} 909 910/* 911 * The plot* functions are based on optimized rasterization primitives written 912 * by Zingl Alois. 913 * See http://members.chello.at/easyfilter/bresenham.html 914 */ 915 916/* 917 * FIXME: 918 * This is a terrible temporary hack. The plot functions below can be adapted 919 * to work like the other rasterization functions but there's no point in doing 920 * that until we know we don't have to write something completely different. 921 */ 922static RegisGraphicsContext *global_context; 923static void 924setPixel(int x, int y) 925{ 926 draw_or_save_patterned_pixel(global_context, x, y); 927} 928 929static void 930plotLine(int x0, int y0, int x1, int y1) 931{ 932 int dx = abs(x1 - x0), sx = x0 < x1 ? 1 : -1; 933 int dy = -abs(y1 - y0), sy = y0 < y1 ? 1 : -1; 934 int err = dx + dy; /* error value e_xy */ 935 936 for (;;) { /* loop */ 937 int e2; 938 setPixel(x0, y0); 939 e2 = 2 * err; 940 if (e2 >= dy) { /* e_xy+e_x > 0 */ 941 if (x0 == x1) 942 break; 943 err += dy; 944 x0 += sx; 945 } 946 if (e2 <= dx) { /* e_xy+e_y < 0 */ 947 if (y0 == y1) 948 break; 949 err += dx; 950 y0 += sy; 951 } 952 } 953} 954 955static void 956plotQuadBezierSeg(int x0, int y0, int x1, int y1, int x2, int y2) 957{ /* plot a limited quadratic Bezier segment */ 958 int sx = x2 - x1; 959 int sy = y2 - y1; 960 long xx = (x0 - x1); /* relative values for checks */ 961 long yy = (y0 - y1); 962 double cur = (double) (xx * sy - yy * sx); /* curvature */ 963 964 assert(xx * sx <= 0 && yy * sy <= 0); /* sign of gradient must not change */ 965 966 if (sx * (long) sx + sy * (long) sy > xx * xx + yy * yy) { /* begin with longer part */ 967 x2 = x0; 968 x0 = sx + x1; 969 y2 = y0; 970 y0 = sy + y1; 971 cur = -cur; /* swap P0 P2 */ 972 } 973 if (cur != 0.0) { /* no straight line */ 974 long xy; 975 double dx, dy, err; 976 977 xx += sx; 978 xx *= (sx = (x0 < x2) ? 1 : -1); /* x step direction */ 979 yy += sy; 980 yy *= (sy = (y0 < y2) ? 1 : -1); /* y step direction */ 981 xy = 2 * xx * yy; 982 xx *= xx; 983 yy *= yy; /* differences 2nd degree */ 984 if (cur * sx * sy < 0) { /* negated curvature? */ 985 xx = -xx; 986 yy = -yy; 987 xy = -xy; 988 cur = -cur; 989 } 990 /* differences 1st degree */ 991 dx = ((4.0 * sy * cur * (x1 - x0)) + (double) xx) - (double) xy; 992 dy = ((4.0 * sx * cur * (y0 - y1)) + (double) yy) - (double) xy; 993 xx += xx; 994 yy += yy; 995 err = dx + dy + (double) xy; /* error 1st step */ 996 do { 997 setPixel(x0, y0); /* plot curve */ 998 if (x0 == x2 && y0 == y2) 999 return; /* last pixel -> curve finished */ 1000 y1 = (2 * err) < dx; /* save value for test of y step */ 1001 if ((2 * err) > dy) { 1002 x0 += sx; 1003 dx -= (double) xy; 1004 dy += (double) yy; 1005 err += dy; 1006 } /* x step */ 1007 if (y1) { 1008 y0 += sy; 1009 dy -= (double) xy; 1010 dx += (double) xx; 1011 err += dx; 1012 } /* y step */ 1013 } while (dy < 0 && dx > 0); /* gradient negates -> algorithm fails */ 1014 } 1015 plotLine(x0, y0, x2, y2); /* plot remaining part to end */ 1016} 1017 1018#if 0 1019static void 1020plotQuadBezier(int x0, int y0, int x1, int y1, int x2, int y2) 1021{ /* plot any quadratic Bezier curve */ 1022 int x = x0 - x1; 1023 int y = y0 - y1; 1024 double t = x0 - 2 * x1 + x2; 1025 double r; 1026 1027 if ((long) x * (x2 - x1) > 0) { /* horizontal cut at P4? */ 1028 if ((long) y * (y2 - y1) > 0) /* vertical cut at P6 too? */ 1029 if (fabs((y0 - 2 * y1 + y2) / t * x) > abs(y)) { /* which first? */ 1030 x0 = x2; 1031 x2 = x + x1; 1032 y0 = y2; 1033 y2 = y + y1; /* swap points */ 1034 } /* now horizontal cut at P4 comes first */ 1035 t = (x0 - x1) / t; 1036 r = (1 - t) * ((1 - t) * y0 + 2.0 * t * y1) + t * t * y2; /* By(t=P4) */ 1037 t = (x0 * x2 - x1 * x1) * t / (x0 - x1); /* gradient dP4/dx=0 */ 1038 x = ifloor(t + 0.5); 1039 y = ifloor(r + 0.5); 1040 r = (y1 - y0) * (t - x0) / (x1 - x0) + y0; /* intersect P3 | P0 P1 */ 1041 plotQuadBezierSeg(x0, y0, x, ifloor(r + 0.5), x, y); 1042 r = (y1 - y2) * (t - x2) / (x1 - x2) + y2; /* intersect P4 | P1 P2 */ 1043 x0 = x1 = x; 1044 y0 = y; 1045 y1 = ifloor(r + 0.5); /* P0 = P4, P1 = P8 */ 1046 } 1047 if ((long) (y0 - y1) * (y2 - y1) > 0) { /* vertical cut at P6? */ 1048 t = y0 - 2 * y1 + y2; 1049 t = (y0 - y1) / t; 1050 r = (1 - t) * ((1 - t) * x0 + 2.0 * t * x1) + t * t * x2; /* Bx(t=P6) */ 1051 t = (y0 * y2 - y1 * y1) * t / (y0 - y1); /* gradient dP6/dy=0 */ 1052 x = ifloor(r + 0.5); 1053 y = ifloor(t + 0.5); 1054 r = (x1 - x0) * (t - y0) / (y1 - y0) + x0; /* intersect P6 | P0 P1 */ 1055 plotQuadBezierSeg(x0, y0, ifloor(r + 0.5), y, x, y); 1056 r = (x1 - x2) * (t - y2) / (y1 - y2) + x2; /* intersect P7 | P1 P2 */ 1057 x0 = x; 1058 x1 = ifloor(r + 0.5); 1059 y0 = y1 = y; /* P0 = P6, P1 = P7 */ 1060 } 1061 plotQuadBezierSeg(x0, y0, x1, y1, x2, y2); /* remaining part */ 1062} 1063#endif 1064 1065static void 1066plotCubicBezierSeg(int x0, int y0, 1067 double x1, double y1, 1068 double x2, double y2, 1069 int x3, int y3) 1070{ /* plot limited cubic Bezier segment */ 1071 int f, fx, fy, tt; 1072 int leg = 1; 1073 int sx = x0 < x3 ? 1 : -1; 1074 int sy = y0 < y3 ? 1 : -1; /* step direction */ 1075 double xc = -fabs(x0 + x1 - x2 - x3); 1076 double xa = xc - 4 * sx * (x1 - x2); 1077 double xb = sx * (x0 - x1 - x2 + x3); 1078 double yc = -fabs(y0 + y1 - y2 - y3); 1079 double ya = yc - 4 * sy * (y1 - y2); 1080 double yb = sy * (y0 - y1 - y2 + y3); 1081 double ab, ac, bc, cb, xx, xy, yy, dx, dy, ex, *pxy; 1082 double EP = 0.01; 1083 /* check for curve restrains */ 1084 /* slope P0-P1 == P2-P3 and (P0-P3 == P1-P2 or no slope change) */ 1085 assert((x1 - x0) * (x2 - x3) < EP && 1086 ((x3 - x0) * (x1 - x2) < EP || xb * xb < xa * xc + EP)); 1087 assert((y1 - y0) * (y2 - y3) < EP && 1088 ((y3 - y0) * (y1 - y2) < EP || yb * yb < ya * yc + EP)); 1089 1090 if (xa == 0.0 && ya == 0.0) { /* quadratic Bezier */ 1091 sx = ifloor((3 * x1 - x0 + 1) / 2); 1092 sy = ifloor((3 * y1 - y0 + 1) / 2); /* new midpoint */ 1093 plotQuadBezierSeg(x0, y0, sx, sy, x3, y3); 1094 return; 1095 } 1096 x1 = (x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0) + 1; /* line lengths */ 1097 x2 = (x2 - x3) * (x2 - x3) + (y2 - y3) * (y2 - y3) + 1; 1098 do { /* loop over both ends */ 1099 ab = xa * yb - xb * ya; 1100 ac = xa * yc - xc * ya; 1101 bc = xb * yc - xc * yb; 1102 ex = ab * (ab + ac - 3 * bc) + ac * ac; /* P0 part of self-intersection loop? */ 1103 f = ((ex > 0.0) 1104 ? 1 1105 : isqrt(1 + 1024 / x1)); /* calculate resolution */ 1106 ab *= f; 1107 ac *= f; 1108 bc *= f; 1109 ex *= f * f; /* increase resolution */ 1110 xy = 9 * (ab + ac + bc) / 8; 1111 cb = 8 * (xa - ya); /* init differences of 1st degree */ 1112 dx = 27 * (8 * ab * (yb * yb - ya * yc) + 1113 ex * (ya + 2 * yb + yc)) / 64 - ya * ya * (xy - ya); 1114 dy = 27 * (8 * ab * (xb * xb - xa * xc) - 1115 ex * (xa + 2 * xb + xc)) / 64 - xa * xa * (xy + xa); 1116 /* init differences of 2nd degree */ 1117 xx = 3 * (3 * ab * (3 * yb * yb - ya * ya - 2 * ya * yc) - 1118 ya * (3 * ac * (ya + yb) + ya * cb)) / 4; 1119 yy = 3 * (3 * ab * (3 * xb * xb - xa * xa - 2 * xa * xc) - 1120 xa * (3 * ac * (xa + xb) + xa * cb)) / 4; 1121 xy = xa * ya * (6 * ab + 6 * ac - 3 * bc + cb); 1122 ac = ya * ya; 1123 cb = xa * xa; 1124 xy = 3 * (xy + 9 * f * (cb * yb * yc - xb * xc * ac) - 1125 18 * xb * yb * ab) / 8; 1126 1127 if (ex < 0) { /* negate values if inside self-intersection loop */ 1128 dx = -dx; 1129 dy = -dy; 1130 xx = -xx; 1131 yy = -yy; 1132 xy = -xy; 1133 ac = -ac; 1134 cb = -cb; 1135 } /* init differences of 3rd degree */ 1136 ab = 6 * ya * ac; 1137 ac = -6 * xa * ac; 1138 bc = 6 * ya * cb; 1139 cb = -6 * xa * cb; 1140 dx += xy; 1141 ex = dx + dy; 1142 dy += xy; /* error of 1st step */ 1143 1144 for (pxy = &xy, fx = fy = f; x0 != x3 && y0 != y3;) { 1145 setPixel(x0, y0); /* plot curve */ 1146 do { /* move sub-steps of one pixel */ 1147 if (dx > *pxy || dy < *pxy) 1148 goto exit; /* confusing values */ 1149 y1 = 2 * ex - dy; /* save value for test of y step */ 1150 if (2 * ex >= dx) { /* x sub-step */ 1151 fx--; 1152 ex += dx += xx; 1153 dy += xy += ac; 1154 yy += bc; 1155 xx += ab; 1156 } 1157 if (y1 <= 0) { /* y sub-step */ 1158 fy--; 1159 ex += dy += yy; 1160 dx += xy += bc; 1161 xx += ac; 1162 yy += cb; 1163 } 1164 } while (fx > 0 && fy > 0); /* pixel complete? */ 1165 if (2 * fx <= f) { 1166 x0 += sx; 1167 fx += f; 1168 } /* x step */ 1169 if (2 * fy <= f) { 1170 y0 += sy; 1171 fy += f; 1172 } /* y step */ 1173 if (pxy == &xy && dx < 0 && dy > 0) 1174 pxy = &EP; /* pixel ahead valid */ 1175 } 1176 exit: 1177 EXCHANGE(x0, x3, tt); 1178 sx = -sx; 1179 xb = -xb; /* swap legs */ 1180 EXCHANGE(y0, y3, tt); 1181 sy = -sy; 1182 yb = -yb; 1183 x1 = x2; 1184 } while (leg--); /* try other end */ 1185 plotLine(x0, y0, x3, y3); /* remaining part in case of cusp or crunode */ 1186} 1187 1188static void 1189plotCubicBezier(int x0, int y0, int x1, int y1, 1190 int x2, int y2, int x3, int y3) 1191{ /* plot any cubic Bezier curve */ 1192 int n = 0, i = 0; 1193 long xc = x0 + x1 - x2 - x3; 1194 long xa = xc - 4 * (x1 - x2); 1195 long xb = x0 - x1 - x2 + x3; 1196 long xd = xb + 4 * (x1 + x2); 1197 long yc = y0 + y1 - y2 - y3; 1198 long ya = yc - 4 * (y1 - y2); 1199 long yb = y0 - y1 - y2 + y3; 1200 long yd = yb + 4 * (y1 + y2); 1201 double fx0 = x0; 1202 double fy0 = y0; 1203 double t1 = (double) (xb * xb - xa * xc), t2, t[5]; 1204 1205#ifdef DEBUG_BEZIER 1206 printf("plotCubicBezier(%d,%d, %d,%d, %d,%d, %d,%d\n", 1207 x0, y0, x1, y1, x2, y2, x3, y3); 1208#endif 1209 /* sub-divide curve at gradient sign changes */ 1210 if (xa == 0) { /* horizontal */ 1211 if (labs(xc) < 2 * labs(xb)) 1212 t[n++] = (double) xc / (2.0 * (double) xb); /* one change */ 1213 } else if (t1 > 0.0) { /* two changes */ 1214 t2 = sqrt(t1); 1215 t1 = ((double) xb - t2) / (double) xa; 1216 if (fabs(t1) < 1.0) 1217 t[n++] = t1; 1218 t1 = ((double) xb + t2) / (double) xa; 1219 if (fabs(t1) < 1.0) 1220 t[n++] = t1; 1221 } 1222 t1 = (double) (yb * yb - ya * yc); 1223 if (ya == 0) { /* vertical */ 1224 if (labs(yc) < 2 * labs(yb)) 1225 t[n++] = (double) yc / (2.0 * (double) yb); /* one change */ 1226 } else if (t1 > 0.0) { /* two changes */ 1227 t2 = sqrt(t1); 1228 t1 = ((double) yb - t2) / (double) ya; 1229 if (fabs(t1) < 1.0) 1230 t[n++] = t1; 1231 t1 = ((double) yb + t2) / (double) ya; 1232 if (fabs(t1) < 1.0) 1233 t[n++] = t1; 1234 } 1235 for (i = 1; i < n; i++) /* bubble sort of 4 points */ 1236 if ((t1 = t[i - 1]) > t[i]) { 1237 t[i - 1] = t[i]; 1238 t[i] = t1; 1239 i = 0; 1240 } 1241 1242 t1 = -1.0; 1243 t[n] = 1.0; /* begin / end point */ 1244 for (i = 0; i <= n; i++) { /* plot each segment separately */ 1245 double fx1, fx2, fx3; 1246 double fy1, fy2, fy3; 1247 1248 t2 = t[i]; /* sub-divide at t[i-1], t[i] */ 1249 fx1 = (t1 * (t1 * (double) xb - (double) (2 * xc)) - 1250 t2 * (t1 * (t1 * (double) xa - (double) (2 * xb)) + (double) 1251 xc) + (double) xd) / 8 - fx0; 1252 fy1 = (t1 * (t1 * (double) yb - (double) (2 * yc)) - 1253 t2 * (t1 * (t1 * (double) ya - (double) (2 * yb)) + (double) 1254 yc) + (double) yd) / 8 - fy0; 1255 fx2 = (t2 * (t2 * (double) xb - (double) (2 * xc)) - 1256 t1 * (t2 * (t2 * (double) xa - (double) (2 * xb)) + (double) 1257 xc) + (double) xd) / 8 - fx0; 1258 fy2 = (t2 * (t2 * (double) yb - (double) (2 * yc)) - 1259 t1 * (t2 * (t2 * (double) ya - (double) (2 * yb)) + (double) 1260 yc) + (double) yd) / 8 - fy0; 1261 fx0 -= fx3 = (t2 * (t2 * ((double) (3 * xb) - t2 * (double) xa) - 1262 (double) (3 * xc)) + (double) xd) / 8; 1263 fy0 -= fy3 = (t2 * (t2 * ((double) (3 * yb) - t2 * (double) ya) - 1264 (double) (3 * yc)) + (double) yd) / 8; 1265 x3 = ifloor(fx3 + 0.5); 1266 y3 = ifloor(fy3 + 0.5); /* scale bounds to int */ 1267 if (fx0 != 0.0) { 1268 fx1 *= fx0 = (x0 - x3) / fx0; 1269 fx2 *= fx0; 1270 } 1271 if (fy0 != 0.0) { 1272 fy1 *= fy0 = (y0 - y3) / fy0; 1273 fy2 *= fy0; 1274 } 1275 if (x0 != x3 || y0 != y3) /* segment t1 - t2 */ 1276 plotCubicBezierSeg(x0, y0, 1277 x0 + fx1, y0 + fy1, 1278 x0 + fx2, y0 + fy2, 1279 x3, y3); 1280 x0 = x3; 1281 y0 = y3; 1282 fx0 = fx3; 1283 fy0 = fy3; 1284 t1 = t2; 1285 } 1286} 1287 1288#if 0 1289static void 1290plotQuadSpline(int n, int x[], int y[], int skip_segments) 1291{ /* plot quadratic spline, destroys input arrays x,y */ 1292#define M_MAX 12 1293 double mi = 1, m[M_MAX]; /* diagonal constants of matrix */ 1294 int i, x0, y0, x1, y1, x2, y2; 1295#ifdef DEBUG_SPLINE_SEGMENTS 1296 int color = 0; 1297#endif 1298 1299 assert(n > 1); /* need at least 3 points P[0]..P[n] */ 1300 1301#ifdef DEBUG_SPLINE_POINTS 1302 { 1303 int save_pattern; 1304 1305 i = 0; 1306 global_context->temporary_write_controls.foreground = 11; 1307 save_pattern = global_context->temporary_write_controls.pattern; 1308 global_context->temporary_write_controls.pattern = 0xff; 1309 draw_patterned_arc(global_context, x[i], y[i], x[i] + 2, y[i], 0, 1310 3600, NULL, NULL); 1311 i++; 1312 global_context->temporary_write_controls.foreground = 15; 1313 for (; i < n; i++) { 1314 draw_patterned_arc(global_context, 1315 x[i], y[i], 1316 x[i] + 2, y[i], 1317 0, 3600, NULL, NULL); 1318 } 1319 global_context->temporary_write_controls.foreground = 10; 1320 draw_patterned_arc(global_context, x[i], y[n], x[i] + 2, y[i], 0, 1321 3600, NULL, NULL); 1322 global_context->temporary_write_controls.pattern = save_pattern; 1323 } 1324#endif 1325 1326 x2 = x[n]; 1327 y2 = y[n]; 1328 1329 x[1] = x0 = 8 * x[1] - 2 * x[0]; /* first row of matrix */ 1330 y[1] = y0 = 8 * y[1] - 2 * y[0]; 1331 1332 for (i = 2; i < n; i++) { /* forward sweep */ 1333 if (i - 2 < M_MAX) 1334 m[i - 2] = mi = 1.0 / (6.0 - mi); 1335 x[i] = x0 = ifloor(8 * x[i] - x0 * mi + 0.5); /* store yi */ 1336 y[i] = y0 = ifloor(8 * y[i] - y0 * mi + 0.5); 1337 } 1338 x1 = ifloor((x0 - 2 * x2) / (5.0 - mi) + 0.5); /* correction last row */ 1339 y1 = ifloor((y0 - 2 * y2) / (5.0 - mi) + 0.5); 1340 1341 for (i = n - 2; i > 0; i--) { /* back substitution */ 1342 if (i <= M_MAX) 1343 mi = m[i - 1]; 1344 x0 = ifloor((x[i] - x1) * mi + 0.5); /* next corner */ 1345 y0 = ifloor((y[i] - y1) * mi + 0.5); 1346#ifdef DEBUG_SPLINE_SEGMENTS 1347 color++; 1348 global_context->temporary_write_controls.foreground = color; 1349#endif 1350 if ((n - 2) - i < skip_segments) 1351 plotQuadBezier((x0 + x1) / 2, (y0 + y1) / 2, x1, y1, x2, y2); 1352 x2 = (x0 + x1) / 2; 1353 x1 = x0; 1354 y2 = (y0 + y1) / 2; 1355 y1 = y0; 1356 } 1357#ifdef DEBUG_SPLINE_SEGMENTS 1358 color++; 1359 global_context->temporary_write_controls.foreground = color; 1360#endif 1361 if (skip_segments > 0) 1362 plotQuadBezier(x[0], y[0], x1, y1, x2, y2); 1363} 1364#endif 1365 1366static void 1367plotCubicSpline(int n, int x[], int y[], int skip_first_last) 1368{ 1369#define M_MAX 12 1370 double mi = 0.25, m[M_MAX]; /* diagonal constants of matrix */ 1371 int x3, y3, x4, y4; 1372 int i, x0, y0, x1, y1, x2, y2; 1373#ifdef DEBUG_SPLINE_SEGMENTS 1374 RegisterNum color = 0; 1375#endif 1376 1377 assert(n > 2); /* need at least 4 points P[0]..P[n] */ 1378 1379#ifdef DEBUG_SPLINE_POINTS 1380 { 1381 unsigned save_pattern; 1382 1383 i = 0; 1384 global_context->temporary_write_controls.foreground = 11; 1385 save_pattern = global_context->temporary_write_controls.pattern; 1386 global_context->temporary_write_controls.pattern = 0xff; 1387 draw_patterned_arc(global_context, x[i], y[i], x[i] + 2, y[i], 0, 1388 3600, NULL, NULL); 1389 i++; 1390 global_context->temporary_write_controls.foreground = 15; 1391 for (; i < n; i++) { 1392 draw_patterned_arc(global_context, 1393 x[i], y[i], 1394 x[i] + 2, y[i], 1395 0, 3600, NULL, NULL); 1396 } 1397 global_context->temporary_write_controls.foreground = 10; 1398 draw_patterned_arc(global_context, x[i], y[i], x[i] + 2, y[i], 0, 1399 3600, NULL, NULL); 1400 global_context->temporary_write_controls.pattern = save_pattern; 1401 } 1402#endif 1403 1404 x3 = x[n - 1]; 1405 y3 = y[n - 1]; 1406 x4 = x[n]; 1407 y4 = y[n]; 1408 1409 x[1] = x0 = 12 * x[1] - 3 * x[0]; /* first row of matrix */ 1410 y[1] = y0 = 12 * y[1] - 3 * y[0]; 1411 1412 for (i = 2; i < n; i++) { /* forward sweep */ 1413 if (i - 2 < M_MAX) 1414 m[i - 2] = mi = 0.25 / (2.0 - mi); 1415 x[i] = x0 = ifloor(12 * x[i] - 2 * x0 * mi + 0.5); 1416 y[i] = y0 = ifloor(12 * y[i] - 2 * y0 * mi + 0.5); 1417 } 1418 x2 = ifloor((x0 - 3 * x4) / (7 - 4 * mi) + 0.5); /* correct last row */ 1419 /* printf("y0=%d, y4=%d mi=%g\n", y0, y4, mi); */ 1420 y2 = ifloor((y0 - 3 * y4) / (7 - 4 * mi) + 0.5); 1421 /* printf("y2=%d, y3=%d, y4=%d\n", y2, y3, y4); */ 1422#ifdef DEBUG_SPLINE_SEGMENTS 1423 color++; 1424 global_context->temporary_write_controls.foreground = color; 1425#endif 1426 if (!skip_first_last) 1427 plotCubicBezier(x3, y3, (x2 + x4) / 2, (y2 + y4) / 2, x4, y4, x4, y4); 1428 1429 if (n - 3 < M_MAX) 1430 mi = m[n - 3]; 1431 x1 = ifloor((x[n - 2] - 2 * x2) * mi + 0.5); 1432 y1 = ifloor((y[n - 2] - 2 * y2) * mi + 0.5); 1433 for (i = n - 3; i > 0; i--) { /* back substitution */ 1434 if (i <= M_MAX) 1435 mi = m[i - 1]; 1436 x0 = ifloor((x[i] - 2 * x1) * mi + 0.5); 1437 y0 = ifloor((y[i] - 2 * y1) * mi + 0.5); 1438 x4 = ifloor((x0 + 4 * x1 + x2 + 3) / 6.0); /* reconstruct P[i] */ 1439 y4 = ifloor((y0 + 4 * y1 + y2 + 3) / 6.0); 1440#ifdef DEBUG_SPLINE_SEGMENTS 1441 color++; 1442 global_context->temporary_write_controls.foreground = color; 1443#endif 1444#define CB_PARM(num) ifloor((num) / 3.0 + 0.5) 1445 plotCubicBezier(x4, y4, 1446 CB_PARM(2 * x1 + x2), 1447 CB_PARM(2 * y1 + y2), 1448 CB_PARM(x1 + 2 * x2), 1449 CB_PARM(y1 + 2 * y2), 1450 x3, y3); 1451 x3 = x4; 1452 y3 = y4; 1453 x2 = x1; 1454 y2 = y1; 1455 x1 = x0; 1456 y1 = y0; 1457 } 1458 x0 = x[0]; 1459 x4 = ifloor((3 * x0 + 7 * x1 + 2 * x2 + 6) / 12.0); /* reconstruct P[1] */ 1460 y0 = y[0]; 1461 y4 = ifloor((3 * y0 + 7 * y1 + 2 * y2 + 6) / 12.0); 1462#ifdef DEBUG_SPLINE_SEGMENTS 1463 global_context->temporary_write_controls.foreground = 4; 1464#endif 1465 plotCubicBezier(x4, y4, 1466 CB_PARM(2 * x1 + x2), 1467 CB_PARM(2 * y1 + y2), 1468 CB_PARM(x1 + 2 * x2), 1469 CB_PARM(y1 + 2 * y2), 1470 x3, y3); 1471#ifdef DEBUG_SPLINE_SEGMENTS 1472 color++; 1473 global_context->temporary_write_controls.foreground = color; 1474#endif 1475 if (!skip_first_last) 1476 plotCubicBezier(x0, y0, x0, y0, (x0 + x1) / 2, (y0 + y1) / 2, x4, y4); 1477} 1478 1479static unsigned 1480find_free_alphabet_index(RegisGraphicsContext *context, unsigned alphabet, 1481 unsigned pixw, unsigned pixh) 1482{ 1483 unsigned ii, jj; 1484 1485 /* try an exact match */ 1486 for (ii = 0U; ii < MAX_REGIS_ALPHABETS; ii++) { 1487 if (context->alphabets[ii].alphabet_num == alphabet && 1488 context->alphabets[ii].pixw == pixw && 1489 context->alphabets[ii].pixh == pixh) { 1490 return ii; 1491 } 1492 } 1493 1494 /* otherwise use any empty slot */ 1495 for (ii = 0U; ii < MAX_REGIS_ALPHABETS; ii++) { 1496 if (context->alphabets[ii].alphabet_num == INVALID_ALPHABET_NUM) { 1497 context->alphabets[ii].alphabet_num = alphabet; 1498 context->alphabets[ii].pixw = pixw; 1499 context->alphabets[ii].pixh = pixh; 1500 return ii; 1501 } 1502 } 1503 1504 /* otherwise recycle a slot with a different font size */ 1505 for (ii = 0U; ii < MAX_REGIS_ALPHABETS; ii++) { 1506 if (context->alphabets[ii].alphabet_num == alphabet) { 1507 context->alphabets[ii].pixw = pixw; 1508 context->alphabets[ii].pixh = pixh; 1509 context->alphabets[ii].name[0] = '\0'; 1510 context->alphabets[ii].fontname[0] = '\0'; 1511 context->alphabets[ii].use_font = 0; 1512 if (context->alphabets[ii].bytes != NULL) { 1513 free(context->alphabets[ii].bytes); 1514 context->alphabets[ii].bytes = NULL; 1515 } 1516 for (jj = 0U; jj < MAX_GLYPHS; jj++) { 1517 context->alphabets[ii].loaded[jj] = 0; 1518 } 1519 return ii; 1520 } 1521 } 1522 1523 /* finally just recycle this arbitrary slot */ 1524 context->alphabets[0U].alphabet_num = alphabet; 1525 context->alphabets[0U].pixw = pixw; 1526 context->alphabets[0U].pixh = pixh; 1527 context->alphabets[0U].name[0] = '\0'; 1528 context->alphabets[0U].fontname[0] = '\0'; 1529 context->alphabets[0U].use_font = 0; 1530 if (context->alphabets[0U].bytes != NULL) { 1531 free(context->alphabets[0U].bytes); 1532 context->alphabets[0U].bytes = NULL; 1533 } 1534 for (jj = 0U; jj < MAX_GLYPHS; jj++) { 1535 context->alphabets[0U].loaded[jj] = 0; 1536 } 1537 1538 return 0U; 1539} 1540 1541#ifdef DEBUG_SPECIFIC_CHAR_METRICS 1542static void 1543dump_bitmap_pixels(Char const *pixels, unsigned w, unsigned h) 1544{ 1545 unsigned yy, xx; 1546 1547 for (yy = 0U; yy < h; yy++) { 1548 printf(" "); 1549 for (xx = 0U; xx < w; xx++) { 1550 if (pixels[yy * w + xx]) { 1551 printf("#"); 1552 } else { 1553 printf("_"); 1554 } 1555 } 1556 printf("\n"); 1557 } 1558} 1559#endif 1560 1561#if OPT_RENDERFONT && defined(HAVE_TYPE_FCCHAR32) 1562static int 1563copy_bitmap_from_xft_font(XtermWidget xw, XftFont *font, FcChar32 ch, 1564 Char *pixels, unsigned w, unsigned h, 1565 unsigned xmin, unsigned ymin) 1566{ 1567 /* 1568 * FIXME: cache: 1569 * - the bitmap for the last M characters and target dimensions 1570 * - reuse the pixmap object where possible 1571 */ 1572 Display *display = XtDisplay(xw); 1573 Screen *screen = XtScreen(xw); 1574 XftColor bg, fg; 1575 Pixmap bitmap; 1576 XftDraw *draw; 1577 XImage *image; 1578 GC glyph_gc; 1579 unsigned bmw, bmh; 1580 unsigned xx, yy; 1581 1582 bmw = w + xmin; 1583 bmh = h; 1584 if (bmw < 1 || bmh < 1) { 1585 TRACE(("refusing impossible bitmap size w=%d h=%d xmin=%d ymin=%d for ch='%c'\n", 1586 bmw, bmh, xmin, ymin, ch)); 1587 return 0; 1588 } 1589 bitmap = XCreatePixmap(display, 1590 DefaultRootWindow(display), 1591 bmw, bmh, (unsigned) getVisualDepth(xw)); 1592 if (bitmap == None) { 1593 TRACE(("unable to create Pixmap for Xft\n")); 1594 return 0; 1595 } 1596 draw = XftDrawCreate(display, bitmap, xw->visInfo->visual, 1597 XDefaultColormap(display, 1598 XScreenNumberOfScreen(screen))); 1599 if (!draw) { 1600 TRACE(("unable to create XftDraw\n")); 1601 XFreePixmap(display, bitmap); 1602 return 0; 1603 } 1604 1605 bg.pixel = 0UL; 1606 bg.color.red = 0; 1607 bg.color.green = 0; 1608 bg.color.blue = 0; 1609 bg.color.alpha = 0x0; 1610 XftDrawRect(draw, &bg, 0, 0, bmw, bmh); 1611 1612 fg.pixel = 1UL; 1613 fg.color.red = 0xffff; 1614 fg.color.green = 0xffff; 1615 fg.color.blue = 0xffff; 1616 fg.color.alpha = 0xffff; 1617 XftDrawString32(draw, &fg, font, -(int) xmin, font->ascent - (int) ymin, 1618 &ch, 1); 1619 1620 glyph_gc = XCreateGC(display, bitmap, 0UL, NULL); 1621 if (!glyph_gc) { 1622 TRACE(("unable to create GC\n")); 1623 XftDrawDestroy(draw); 1624 XFreePixmap(display, bitmap); 1625 return 0; 1626 } 1627 XSetForeground(display, glyph_gc, 1UL); 1628 XSetBackground(display, glyph_gc, 0UL); 1629 image = XGetImage(display, bitmap, 0, 0, w, h, 1UL, XYPixmap); 1630 if (!image) { 1631 TRACE(("unable to create XImage\n")); 1632 XFreeGC(display, glyph_gc); 1633 XftDrawDestroy(draw); 1634 XFreePixmap(display, bitmap); 1635 return 0; 1636 } 1637 1638 for (yy = 0U; yy < h; yy++) { 1639#ifdef DEBUG_XFT_GLYPH_COPY 1640 TRACE(("'%c'[%02u]:", ch, yy)); 1641#endif 1642 for (xx = 0U; xx < w; xx++) { 1643 unsigned long pix; 1644 pix = XGetPixel(image, (int) xx, (int) yy); 1645 pixels[yy * w + xx] = (unsigned char) pix; 1646#ifdef DEBUG_XFT_GLYPH_COPY 1647 TRACE((" %lu", pix)); 1648#endif 1649 } 1650#ifdef DEBUG_XFT_GLYPH_COPY 1651 TRACE(("\n")); 1652#endif 1653 } 1654 1655 XFreeGC(display, glyph_gc); 1656 XDestroyImage(image); 1657 XftDrawDestroy(draw); 1658 XFreePixmap(display, bitmap); 1659 return 1; 1660} 1661 1662static void 1663get_xft_glyph_dimensions(XtermWidget xw, XftFont *font, unsigned *w, 1664 unsigned *h, unsigned *xmin, unsigned *ymin) 1665{ 1666 unsigned workw, workh; 1667 FcChar32 ch; 1668 Char *pixels; 1669 Char *pixelp; 1670 unsigned yy, xx; 1671 unsigned char_count, pixel_count; 1672 unsigned real_minx, real_maxx, real_miny, real_maxy; 1673 unsigned char_minx, char_maxx, char_miny, char_maxy; 1674 1675 /* 1676 * For each ASCII or ISO-8859-1 printable code, find out what its 1677 * dimensions are. 1678 * 1679 * We actually render the glyphs and determine the extents ourselves 1680 * because the font library can lie by several pixels, and since we are 1681 * doing manual character placement in fixed areas the glyph boundary needs 1682 * to be accurate. 1683 * 1684 * Ignore control characters and spaces - their extent information is 1685 * misleading. 1686 */ 1687 1688 /* Our "work area" is just a buffer which should be big enough to hold the 1689 * largest glyph even if its size is under-reported by a couple of pixels 1690 * in each dimension. 1691 */ 1692 workw = (unsigned) font->max_advance_width + 2U; 1693 if (font->ascent + font->descent > font->height) { 1694 workh = (unsigned) (font->ascent + font->descent) + 2U; 1695 } else { 1696 workh = (unsigned) font->height + 2U; 1697 } 1698 1699 if (!(pixels = malloc((size_t) (workw * workh)))) { 1700 *w = 0U; 1701 *h = 0U; 1702#ifdef DEBUG_COMPUTED_FONT_METRICS 1703 TRACE(("reported metrics:\n")); 1704 TRACE((" %ux%u ascent=%u descent=%u\n", font->max_advance_width, 1705 font->height, font->ascent, font->descent)); 1706 TRACE(("computed metrics:\n")); 1707 TRACE((" (unable to allocate pixel array)\n")); 1708#endif 1709 return; 1710 } 1711 1712 /* FIXME: ch is in UCS32 -- try to support non-ASCII characters */ 1713 char_count = 0U; 1714 real_minx = workw - 1U; 1715 real_maxx = 0U; 1716 real_miny = workh - 1U; 1717 real_maxy = 0U; 1718 for (ch = 33; ch < 256; ++ch) { 1719 if (ch >= 127 && ch <= 160) { 1720#ifdef DEBUG_SPECIFIC_CHAR_METRICS 1721 if (IS_DEBUG_CHAR(ch)) 1722 printf("char: '%c' not in interesting range; ignoring\n", 1723 (char) ch); 1724#endif 1725 continue; 1726 } 1727 if (!FcCharSetHasChar(font->charset, ch)) { 1728#ifdef DEBUG_SPECIFIC_CHAR_METRICS 1729 if (IS_DEBUG_CHAR(ch)) 1730 printf("char: '%c' not in charset; ignoring\n", (char) ch); 1731#endif 1732 continue; 1733 } 1734 1735 if (!copy_bitmap_from_xft_font(xw, font, ch, pixels, 1736 workw, workh, 0U, 0U)) { 1737#ifdef DEBUG_SPECIFIC_CHAR_METRICS 1738 if (IS_DEBUG_CHAR(ch)) 1739 printf("char: '%c' bitmap could not be copied; ignoring\n", 1740 (char) ch); 1741#endif 1742 continue; 1743 } 1744 1745 pixel_count = 0U; 1746 char_minx = workh - 1U; 1747 char_maxx = 0U; 1748 char_miny = workh - 1U; 1749 char_maxy = 0U; 1750 pixelp = pixels; 1751 for (yy = 0U; yy < workh; yy++) { 1752 for (xx = 0U; xx < workw; xx++) { 1753 if (*pixelp++) { 1754 if (xx < char_minx) 1755 char_minx = xx; 1756 else if (xx > char_maxx) 1757 char_maxx = xx; 1758 if (yy < char_miny) 1759 char_miny = yy; 1760 else if (yy > char_maxy) 1761 char_maxy = yy; 1762 pixel_count++; 1763 } 1764 } 1765 } 1766 if (pixel_count < 1U) { 1767#ifdef DEBUG_SPECIFIC_CHAR_METRICS 1768 if (IS_DEBUG_CHAR(ch)) 1769 printf("char: '%c' has no pixels; ignoring\n", (char) ch); 1770#endif 1771 continue; 1772 } 1773#ifdef DEBUG_SPECIFIC_CHAR_METRICS 1774 if (IS_DEBUG_CHAR(ch)) { 1775 printf("char: '%c' (%d)\n", (char) ch, ch); 1776 printf(" minx: %u\n", char_minx); 1777 printf(" maxx: %u\n", char_maxx); 1778 printf(" miny: %u\n", char_miny); 1779 printf(" maxy: %u\n", char_maxy); 1780 dump_bitmap_pixels(pixels, workw, workh); 1781 printf("\n"); 1782 } 1783#endif 1784 1785 if (char_minx < real_minx) 1786 real_minx = char_minx; 1787 if (char_maxx > real_maxx) 1788 real_maxx = char_maxx; 1789 if (char_miny < real_miny) 1790 real_miny = char_miny; 1791 if (char_maxy > real_maxy) 1792 real_maxy = char_maxy; 1793 char_count++; 1794 } 1795 1796 free(pixels); 1797 1798 if (char_count < 1U) { 1799#ifdef DEBUG_COMPUTED_FONT_METRICS 1800 TRACE(("reported metrics:\n")); 1801 TRACE((" %ux%u ascent=%u descent=%u\n", font->max_advance_width, 1802 font->height, font->ascent, font->descent)); 1803 TRACE(("computed metrics:\n")); 1804 TRACE((" (no characters found)\n")); 1805#endif 1806 *w = 0U; 1807 *h = 0U; 1808 return; 1809 } 1810 1811 *w = (unsigned) (1 + real_maxx - real_minx); 1812 *h = (unsigned) (1 + real_maxy - real_miny); 1813 *xmin = real_minx; 1814 *ymin = real_miny; 1815 1816#ifdef DEBUG_COMPUTED_FONT_METRICS 1817 printf("reported metrics:\n"); 1818 printf(" %ux%u ascent=%u descent=%u\n", font->max_advance_width, 1819 font->height, font->ascent, font->descent); 1820 printf("computed metrics:\n"); 1821 printf(" real_minx=%u real_maxx=%u real_miny=%u real_maxy=%u\n", 1822 real_minx, real_maxx, real_miny, real_maxy); 1823 printf(" final: %ux%u xmin=%u ymin=%u\n", *w, *h, *xmin, *ymin); 1824#endif 1825} 1826 1827#define FONT_SIZE_CACHE_SIZE 32U 1828 1829/* Find the font pixel size which returns the font which is closest to the given 1830 * maxw and maxh without overstepping either dimension. 1831 */ 1832static XftFont * 1833find_best_xft_font_size(XtermWidget xw, 1834 char const *fontname, 1835 unsigned maxw, unsigned maxh, unsigned max_pixels, 1836 unsigned *w, unsigned *h, 1837 unsigned *xmin, unsigned *ymin) 1838{ 1839 Display *display = XtDisplay(xw); 1840 Screen *screen = XtScreen(xw); 1841 XftFont *font; 1842 unsigned targeth; 1843 unsigned ii, cacheindex; 1844 /* FIXME: change cache to just cache the final result and put it in a 1845 * wrapper function 1846 */ 1847 static struct { 1848 char fontname[REGIS_FONTNAME_LEN]; 1849 unsigned maxw, maxh, max_pixels; 1850 unsigned targeth; 1851 unsigned w, h; 1852 unsigned xmin; 1853 unsigned ymin; 1854 } cache[FONT_SIZE_CACHE_SIZE]; 1855 1856 assert(display); 1857 assert(screen); 1858 assert(fontname); 1859 assert(w); 1860 assert(h); 1861 assert(xmin); 1862 assert(ymin); 1863 1864#ifdef DEBUG_FONT_SIZE_SEARCH 1865 TRACE(("determining best size of font '%s' for %ux%u glyph with max_pixels=%u\n", 1866 fontname, maxw, maxh, max_pixels)); 1867#endif 1868 cacheindex = FONT_SIZE_CACHE_SIZE; 1869 for (ii = 0U; ii < FONT_SIZE_CACHE_SIZE; ii++) { 1870 if (cache[ii].maxw == maxw && cache[ii].maxh == maxh && 1871 cache[ii].max_pixels == max_pixels && 1872 strcmp(cache[ii].fontname, fontname) == 0) { 1873 cacheindex = ii; 1874 break; 1875 } 1876 } 1877 1878 if (cacheindex < FONT_SIZE_CACHE_SIZE) { 1879 targeth = cache[cacheindex].targeth; 1880 } else { 1881 targeth = maxh * 10U + 5U; 1882 } 1883 for (;;) { 1884 if (targeth <= 5U) { 1885 TRACE(("Giving up finding suitable Xft font size for \"%s\" at %ux%u.\n", 1886 fontname, maxw, maxh)); 1887 return NULL; 1888 } 1889 1890 /* 1891 * Xft does a bad job at: 1892 * - two-color low-resolution anti-aliased fonts 1893 * - non-anti-aliased fonts at low resolution unless a font size is 1894 * given (pixel size does not help, and the value of the font size 1895 * doesn't appear to matter). 1896 * 1897 * In those two cases it literally drops pixels, sometimes whole 1898 * columns, making the glyphs unreadable and at least ugly even when 1899 * readable. 1900 */ 1901 font = NULL; 1902 /* 1903 * FIXME: 1904 * Also, we need to scale the width and height separately. The 1905 * CHAR_WIDTH and CHAR_HEIGHT attributes would seem to be ideal, but 1906 * don't appear to have any effect if set. Instead we will manually 1907 * scale the bitmap later, which may be very ugly because we won't try 1908 * to identify different parts of glyphs or preserve density. 1909 */ 1910 { 1911 XftPattern *pat; 1912 XftPattern *match; 1913 XftResult status; 1914 1915 if ((pat = XftNameParse(fontname))) { 1916#ifdef DEBUG_FONT_SIZE_SEARCH 1917 TRACE(("trying targeth=%g\n", targeth / 10.0)); 1918#endif 1919 XftPatternBuild(pat, 1920#if 0 1921 /* arbitrary value */ 1922 XFT_SIZE, XftTypeDouble, 12.0, 1923#endif 1924 XFT_PIXEL_SIZE, XftTypeDouble, (double) 1925 targeth / 10.0, 1926#if 0 1927 XFT_CHAR_WIDTH, XftTypeInteger, (int) maxw, 1928 XFT_CHAR_HEIGHT, XftTypeInteger, (int) 1929 (targeth / 10U), 1930#endif 1931 XFT_SPACING, XftTypeInteger, XFT_MONO, 1932 XFT_SLANT, XftTypeInteger, 0, 1933 XFT_ANTIALIAS, XftTypeBool, False, 1934 NULL); 1935 if ((match = XftFontMatch(display, 1936 XScreenNumberOfScreen(screen), 1937 pat, &status))) { 1938 font = XftFontOpenPattern(display, match); 1939 maybeXftCache(xw, font); 1940 } 1941 XftPatternDestroy(pat); 1942 } 1943 } 1944 if (!font) { 1945#ifdef DEBUG_FONT_SIZE_SEARCH 1946 { 1947 char buffer[1024]; 1948 1949 if (XftNameUnparse(font->pattern, buffer, (int) sizeof(buffer))) 1950 printf("font name unparsed: \"%s\"\n", buffer); 1951 } 1952#endif 1953 TRACE(("unable to open a monospaced Xft font matching '%s' with pixelsize %g\n", 1954 fontname, targeth / 10.0)); 1955 return NULL; 1956 } 1957#ifdef DEBUG_FONT_SIZE_SEARCH 1958 { 1959 char buffer[1024]; 1960 1961 if (XftNameUnparse(font->pattern, buffer, (int) sizeof(buffer))) { 1962 TRACE(("Testing font named \"%s\"\n", buffer)); 1963 } else { 1964 TRACE(("Testing unknown font\n")); 1965 } 1966 } 1967#endif 1968 1969 if (cacheindex < FONT_SIZE_CACHE_SIZE && 1970 targeth == cache[cacheindex].targeth) { 1971 *w = cache[cacheindex].w; 1972 *h = cache[cacheindex].h; 1973 *xmin = cache[cacheindex].xmin; 1974 *ymin = cache[cacheindex].ymin; 1975 } else { 1976 get_xft_glyph_dimensions(xw, font, w, h, xmin, ymin); 1977 1978 if (*w < 1 || *h < 1) { 1979#ifdef DEBUG_FONT_SIZE_SEARCH 1980 TRACE(("got %ux%u dimensions for target size targeth=%d; trying reduced target size\n", 1981 *w, *h, targeth)); 1982#endif 1983 targeth--; 1984 continue; 1985 } 1986 } 1987#ifdef DEBUG_FONT_SIZE_SEARCH 1988 TRACE(("checking max=%ux%u targeth=%u.%u\n", maxw, maxh, targeth / 1989 10U, targeth % 10U)); 1990#endif 1991 1992 if (*h > maxh) { 1993 XftFontClose(display, font); 1994#ifdef DEBUG_FONT_SIZE_SEARCH 1995 TRACE(("got %ux%u glyph; too tall; reducing target size\n", *w, *h)); 1996#endif 1997 if (*h > 2U * maxh) { 1998 targeth /= (*h / maxh); 1999 } else if (targeth > 10U && *h > maxh + 1U) { 2000 targeth -= 10U; 2001 } else { 2002 targeth--; 2003 } 2004 continue; 2005 } 2006 if (*w > maxw) { 2007 XftFontClose(display, font); 2008#ifdef DEBUG_FONT_SIZE_SEARCH 2009 TRACE(("got %ux%u glyph; too wide; reducing target size\n", *w, *h)); 2010#endif 2011 if (*w > 2U * maxw) { 2012 targeth /= (*w / maxw); 2013 } else if (targeth > 10U && *w > maxw + 1U) { 2014 targeth -= 10U; 2015 } else { 2016 targeth--; 2017 } 2018 continue; 2019 } 2020 if (*w * *h > max_pixels) { 2021 XftFontClose(display, font); 2022#ifdef DEBUG_FONT_SIZE_SEARCH 2023 TRACE(("got %ux%u glyph; too many pixels; reducing target size\n", 2024 *w, *h)); 2025#endif 2026 if (*w * *h > 2U * max_pixels) { 2027 unsigned min = *w < *h ? *w : *h; 2028 unsigned divisor = (*w * *h) / (max_pixels * min); 2029 if (divisor > 1U) { 2030 targeth /= divisor; 2031 } else if (targeth > 10U) { 2032 targeth -= 10U; 2033 } else { 2034 targeth--; 2035 } 2036 } else { 2037 targeth--; 2038 } 2039 continue; 2040 } 2041#ifdef DEBUG_FONT_NAME 2042 { 2043 char buffer[1024]; 2044 2045 if (XftNameUnparse(font->pattern, buffer, (int) sizeof(buffer))) { 2046 TRACE(("Final font for \"%s\" max %dx%d is \"%s\"\n", 2047 fontname, maxw, maxh, buffer)); 2048 } else { 2049 TRACE(("Final font for \"%s\" max %dx%d is unknown\n", 2050 fontname, maxw, maxh)); 2051 } 2052 } 2053#endif 2054 2055 if (cacheindex == FONT_SIZE_CACHE_SIZE) { 2056 for (ii = 0U; ii < FONT_SIZE_CACHE_SIZE; ii++) { 2057 if (cache[ii].maxw == 0U || cache[ii].maxh == 0U || 2058 cache[ii].max_pixels == 0U) { 2059 CopyFontname(cache[ii].fontname, fontname); 2060 cache[ii].maxw = maxw; 2061 cache[ii].maxh = maxh; 2062 cache[ii].max_pixels = max_pixels; 2063 cache[ii].targeth = targeth; 2064 cache[ii].w = *w; 2065 cache[ii].h = *h; 2066 cache[ii].xmin = *xmin; 2067 cache[ii].ymin = *ymin; 2068 break; 2069 } 2070 } 2071 if (ii == FONT_SIZE_CACHE_SIZE) { 2072 ii = targeth % FONT_SIZE_CACHE_SIZE; 2073 CopyFontname(cache[ii].fontname, fontname); 2074 cache[ii].maxw = maxw; 2075 cache[ii].maxh = maxh; 2076 cache[ii].max_pixels = max_pixels; 2077 cache[ii].targeth = targeth; 2078 cache[ii].w = *w; 2079 cache[ii].h = *h; 2080 cache[ii].xmin = *xmin; 2081 cache[ii].ymin = *ymin; 2082 } 2083 } 2084 return font; 2085 } 2086} 2087#endif 2088 2089static int 2090get_xft_bitmap_of_character(RegisGraphicsContext const *context, 2091 char const *fontname, int ch, 2092 unsigned maxw, unsigned maxh, Char *pixels, 2093 unsigned max_pixels, unsigned *w, unsigned *h) 2094{ 2095 /* 2096 * See Xft / RENDERFONT stuff in fontutils.c and used in utils.c 2097 * Add a separate configuration for ReGIS. 2098 */ 2099 /* 2100 * FIXME: cache: 2101 * - reuse the font where possible 2102 */ 2103#ifdef XRENDERFONT 2104 XtermWidget xw = context->destination_graphic->xw; 2105 Display *display = XtDisplay(xw); 2106 XftFont *font; 2107 unsigned xmin = 0U, ymin = 0U; 2108 2109# ifdef DEBUG_XFT_GLYPH_LOADING 2110 TRACE(("trying to load glyph '%c' at max size %dx%d\n", ch, maxw, maxh)); 2111# endif 2112 if (!(font = find_best_xft_font_size(xw, fontname, maxw, maxh, 2113 max_pixels, w, h, &xmin, &ymin))) { 2114 TRACE(("Unable to find suitable Xft font\n")); 2115 return 0; 2116 } 2117 2118 if (*w == 0U || *h == 0U) { 2119 TRACE(("empty glyph found for '%c'\n", ch)); 2120 XftFontClose(display, font); 2121 return 1; 2122 } 2123 2124 if (!copy_bitmap_from_xft_font(xw, font, CharOf(ch), pixels, *w, *h, 2125 xmin, ymin)) { 2126 TRACE(("Unable to create bitmap for '%c'\n", ch)); 2127 XftFontClose(display, font); 2128 return 0; 2129 } 2130 XftFontClose(display, font); 2131# ifdef DEBUG_XFT_GLYPH_LOADING 2132 TRACE(("loaded glyph '%c' at max size %dx%d\n", ch, maxw, maxh)); 2133# endif 2134 2135 return 1; 2136#else 2137 (void) context; 2138 (void) fontname; 2139 (void) ch; 2140 (void) maxw; 2141 (void) maxh; 2142 (void) pixels; 2143 (void) max_pixels; 2144 (void) w; 2145 (void) h; 2146 2147 TRACE(("Not rendering Xft font for ReGIS (support not compiled in).\n")); 2148 return 0; 2149#endif 2150} 2151 2152static unsigned 2153find_best_alphabet_index(RegisGraphicsContext const *context, 2154 unsigned minw, unsigned minh, 2155 unsigned targetw, unsigned targeth, 2156 unsigned max_pixels) 2157{ 2158 unsigned ii; 2159 unsigned bestmatch; 2160 unsigned bestw, besth; 2161 2162 assert(context); 2163 assert(targetw); 2164 assert(targeth); 2165 assert(max_pixels); 2166 2167 bestmatch = MAX_REGIS_ALPHABETS; 2168 bestw = 0U; 2169 besth = 0U; 2170 for (ii = 0U; ii < MAX_REGIS_ALPHABETS; ii++) { 2171 if (context->alphabets[ii].alphabet_num == 2172 context->current_text_controls->alphabet_num && 2173 context->alphabets[ii].pixw >= minw && 2174 context->alphabets[ii].pixh >= minh && 2175 context->alphabets[ii].pixw <= targetw && 2176 context->alphabets[ii].pixh <= targeth && 2177 ((context->alphabets[ii].pixw >= bestw && 2178 context->alphabets[ii].pixh > besth) || 2179 (context->alphabets[ii].pixw > bestw && 2180 context->alphabets[ii].pixh >= besth)) && 2181 context->alphabets[ii].pixw * 2182 context->alphabets[ii].pixh <= max_pixels) { 2183 bestmatch = ii; 2184 bestw = context->alphabets[ii].pixw; 2185 besth = context->alphabets[ii].pixh; 2186 } 2187 } 2188 2189 /* If we can't find one to scale up, look for one to scale down. */ 2190 if (bestmatch == MAX_REGIS_ALPHABETS) { 2191 bestw = max_pixels; 2192 besth = max_pixels; 2193 for (ii = 0U; ii < MAX_REGIS_ALPHABETS; ii++) { 2194 if (context->alphabets[ii].alphabet_num == 2195 context->current_text_controls->alphabet_num && 2196 context->alphabets[ii].pixw >= minw && 2197 context->alphabets[ii].pixh >= minh && 2198 ((context->alphabets[ii].pixw <= bestw && 2199 context->alphabets[ii].pixh < besth) || 2200 (context->alphabets[ii].pixw < bestw && 2201 context->alphabets[ii].pixh <= besth)) && 2202 context->alphabets[ii].pixw * 2203 context->alphabets[ii].pixh <= max_pixels) { 2204 bestmatch = ii; 2205 bestw = context->alphabets[ii].pixw; 2206 besth = context->alphabets[ii].pixh; 2207 } 2208 } 2209 } 2210#ifdef DEBUG_ALPHABET_LOOKUP 2211 if (bestmatch < MAX_REGIS_ALPHABETS) { 2212 TRACE(("for target size %ux%u alphabet %u found index %u size %ux%u font=%s\n", 2213 targetw, targeth, context->current_text_controls->alphabet_num, 2214 bestmatch, 2215 bestw, besth, 2216 context->alphabets[bestmatch].use_font ? 2217 context->alphabets[bestmatch].fontname : "(none)")); 2218 } else { 2219 TRACE(("for target size %ux%u alphabet %u found no suitable alphabets\n", 2220 targetw, targeth, context->current_text_controls->alphabet_num)); 2221 } 2222#endif 2223 2224 return bestmatch; 2225} 2226 2227#define GLYPH_WIDTH_BYTES(PIXW) ( ((PIXW) + 7U) >> 3U ) 2228 2229static int 2230get_user_bitmap_of_character(RegisGraphicsContext const *context, 2231 int ch, 2232 unsigned alphabet_index, 2233 Char *pixels, 2234 unsigned int max_pixels) 2235{ 2236 const Char *glyph; 2237 unsigned w, h; 2238 unsigned xx, yy; 2239 unsigned byte, bit; 2240 2241 assert(context); 2242 assert(pixels); 2243 2244 if (!context->alphabets[alphabet_index].loaded[(Char) ch]) { 2245 TRACE(("BUG: in alphabet %u with alphabet index %u user glyph for '%c' not loaded\n", 2246 context->current_text_controls->alphabet_num, alphabet_index, 2247 ch)); 2248 return 0; 2249 } 2250 2251 assert(context->alphabets[alphabet_index].bytes); 2252 2253 w = context->alphabets[alphabet_index].pixw; 2254 h = context->alphabets[alphabet_index].pixh; 2255 glyph = &context->alphabets[alphabet_index] 2256 .bytes[(Char) ch * GLYPH_WIDTH_BYTES(w) * h]; 2257 2258 if (w * h > max_pixels) { 2259 TRACE(("in alphabet %u with alphabet index %u user glyph for '%c' is too large: %ux%u (max_pixels=%u)\n", 2260 context->current_text_controls->alphabet_num, alphabet_index, 2261 ch, w, h, max_pixels)); 2262 return 0; 2263 } 2264 2265 for (yy = 0U; yy < h; yy++) { 2266 for (xx = 0U; xx < w; xx++) { 2267 byte = yy * GLYPH_WIDTH_BYTES(w) + (xx >> 3U); 2268 bit = xx & 7U; 2269 pixels[yy * w + xx] = (Char) (((unsigned) glyph[byte] 2270 >> (7U - bit)) & 1U); 2271 } 2272 } 2273 2274 return 1; 2275} 2276 2277/* 2278 * alphabets 2279 * 0 built-in 2280 * 1-N custom (max is 3 on VT3X0 -- up to MAX_REGIS_ALPHABETS with xterm) 2281 * 2282 * built-in 7-bit charsets 2283 * (B ASCII 2284 * (0 DEC special graphics 2285 * (> DEC technical 2286 * (A NCR British 2287 * (4 NCR Dutch 2288 * (5 NCR Finnish 2289 * (R NCR French 2290 * (9 NCR French Canadian 2291 * (K NCR German 2292 * (Y NCR Italian 2293 * (' NCR Norwegian/Danish 2294 * (!6 NCR Portuguese 2295 * (Z NCR Spanish 2296 * (7 NCR Swedish 2297 * (- NCR Swiss 2298 * 2299 * -@ ??? 2300 * 2301 * built-in 8-bit charsets 2302 * )%5 DEC supplemental graphics 2303 * -A ISO Latin-1 supplemental 2304 * )< user-preferred supplemental (94 chars) 2305 * 2306 * defaults 2307 * terminal char cell size charsets angle 2308 * VT3x0 S1 0:ASCII(94) 0 (positive) 2309 * 2310 */ 2311static void 2312get_bitmap_of_character(RegisGraphicsContext const *context, int ch, 2313 unsigned maxw, unsigned maxh, Char *pixels, 2314 unsigned *w, unsigned *h, unsigned max_pixels) 2315{ 2316 unsigned bestmatch; 2317 char const *fontname = NULL; 2318 2319 assert(context); 2320 assert(w); 2321 assert(h); 2322 2323#ifdef DEBUG_GLYPH_RETRIEVAL 2324 TRACE(("getting bitmap of glyph %d, current alphabet %d\n", ch, 2325 context->current_text_controls->alphabet_num)); 2326#endif 2327 2328 if (maxw < 1U || maxh < 1U || max_pixels < 1U) { 2329 *w = 0U; 2330 *h = 0U; 2331 return; 2332 } 2333 2334 if (context->current_text_controls->alphabet_num == 0) 2335 fontname = context->builtin_font; 2336 2337 *w = 0U; 2338 *h = 0U; 2339 2340 bestmatch = find_best_alphabet_index(context, 1U, 1U, maxw, maxh, 2341 max_pixels); 2342 if (bestmatch < MAX_REGIS_ALPHABETS) { 2343 RegisAlphabet const *alpha = &context->alphabets[bestmatch]; 2344 2345#ifdef DEBUG_GLYPH_RETRIEVAL 2346 TRACE(("checking user glyph for slot=%u alphabet=%d use_font=%d loaded=%d\n", 2347 bestmatch, alpha->alphabet_num, alpha->use_font, 2348 alpha->loaded[ch])); 2349#endif 2350 if (!alpha->use_font && 2351 get_user_bitmap_of_character(context, ch, bestmatch, pixels, 2352 max_pixels)) { 2353#ifdef DEBUG_GLYPH_RETRIEVAL 2354 TRACE(("found user glyph for alphabet number %d (index %u)\n\n", 2355 alpha->alphabet_num, bestmatch)); 2356#endif 2357 *w = alpha->pixw; 2358 *h = alpha->pixh; 2359 return; 2360 } 2361 2362 if (alpha->use_font) 2363 fontname = alpha->fontname; 2364 } 2365 2366 if (fontname) { 2367#ifdef DEBUG_GLYPH_RETRIEVAL 2368 TRACE(("using xft font %s\n", fontname)); 2369#endif 2370 if (get_xft_bitmap_of_character(context, fontname, ch, 2371 maxw, maxh, pixels, 2372 max_pixels, w, h)) { 2373 if (*w > maxw) { 2374 TRACE(("BUG: Xft glyph is too wide: %ux%u but max is %ux%u\n", 2375 *w, *h, maxw, maxh)); 2376 } else if (*h > maxh) { 2377 TRACE(("BUG: Xft glyph is too tall: %ux%u but max is %ux%u\n", 2378 *w, *h, maxw, maxh)); 2379 } else if (*w * *h > max_pixels) { 2380 TRACE(("BUG: Xft glyph has too many pixels: %u but max is %u\n", 2381 *w * *h, max_pixels)); 2382 } else { 2383 TRACE(("got glyph from \"%s\" for alphabet number %d\n", 2384 fontname, context->current_text_controls->alphabet_num)); 2385#ifdef DEBUG_SPECIFIC_CHAR_METRICS 2386 if (IS_DEBUG_CHAR(ch)) { 2387 printf("got %ux%u Xft bitmap for '%c' target size %ux%u:\n", 2388 *w, *h, 2389 ch, maxw, maxh); 2390 dump_bitmap_pixels(pixels, *w, *h); 2391 printf("\n"); 2392 } 2393#endif 2394 return; 2395 } 2396 } 2397 } 2398 2399 TRACE(("unable to load any bitmap for character '%c' in alphabet number %u at %ux%u\n", 2400 ch, context->current_text_controls->alphabet_num, maxw, maxh)); 2401 2402 /* 2403 * The VT3x0 series (and probably earlier ReGIS implementations) use a solid 2404 * block glyph for unknown glyphs. 2405 */ 2406 { 2407 unsigned xx, yy; 2408 2409 *w = MIN2(8U, maxh); 2410 *h = MIN2(10U, maxw); 2411 for (yy = 0U; yy < *h; yy++) 2412 for (xx = 0U; xx < *w; xx++) 2413 pixels[yy * *w + xx] = '\1'; 2414 } 2415} 2416 2417#define ROT_SHEAR_SCALE 8192 2418#define SIGNED_UNSIGNED_MOD(VAL, BASE) ( (((VAL) % (int) (BASE)) + (int) (BASE)) % (int) (BASE) ) 2419 2420static unsigned 2421get_shade_character_pixel(Char const *pixels, unsigned w, unsigned h, 2422 unsigned smaxf, unsigned scale, int slant_dx, 2423 int px, int py) 2424{ 2425 unsigned wx, wy; 2426 unsigned fx, fy; 2427 2428 wx = (unsigned) SIGNED_UNSIGNED_MOD(px - 2429 (slant_dx * SIGNED_UNSIGNED_MOD(py, smaxf)) 2430 / ROT_SHEAR_SCALE, smaxf); 2431 wy = (unsigned) SIGNED_UNSIGNED_MOD(py, smaxf); 2432 2433 fx = (wx * scale) >> SCALE_FIXED_POINT; 2434 fy = (wy * scale) >> SCALE_FIXED_POINT; 2435 if (fx < w && fy < h) { 2436 return (unsigned) pixels[fy * w + fx]; 2437 } 2438 return 0U; 2439} 2440 2441static void 2442draw_character(RegisGraphicsContext *context, int ch, 2443 int slant_dx, int rot_shear_x, 2444 int rot_shear_y, int x_sign_x, int x_sign_y, 2445 int y_sign_x, int y_sign_y) 2446{ 2447 const unsigned xmaxd = context->current_text_controls->character_display_w; 2448 const unsigned ymaxd = context->current_text_controls->character_display_h; 2449 const unsigned xmaxf = context->current_text_controls->character_unit_cell_w; 2450 const unsigned ymaxf = context->current_text_controls->character_unit_cell_h; 2451 unsigned w, h; 2452 unsigned xscale, yscale; 2453 unsigned fx, fy; 2454 unsigned px, py; 2455 int sx; 2456 int rx, ry; 2457 int ox, oy; 2458 unsigned pad_left, pad_right; 2459 unsigned pad_top, pad_bottom; 2460 Char pixels[MAX_GLYPH_PIXELS]; 2461 unsigned value; 2462 2463 get_bitmap_of_character(context, ch, xmaxf, ymaxf, pixels, &w, &h, 2464 MAX_GLYPH_PIXELS); 2465 if (w < 1 || h < 1) { 2466 return; 2467 } 2468 2469 if (xmaxd > xmaxf) { 2470 pad_left = (xmaxd - xmaxf) / 2U; 2471 pad_right = (xmaxd - xmaxf) - pad_left; 2472 } else { 2473 pad_left = 0U; 2474 pad_right = 0U; 2475 } 2476 if (ymaxd > ymaxf) { 2477 pad_top = (ymaxd - ymaxf) / 2U; 2478 pad_bottom = (ymaxd - ymaxf) - pad_top; 2479 } else { 2480 pad_top = 0U; 2481 pad_bottom = 0U; 2482 } 2483 2484 xscale = (w << SCALE_FIXED_POINT) / xmaxf; 2485 yscale = (h << SCALE_FIXED_POINT) / ymaxf; 2486 2487 for (py = 0U; py < ymaxd; py++) { 2488 for (px = 0U; px < xmaxd; px++) { 2489 if (py < pad_top || px < pad_left || 2490 py >= ymaxd - pad_bottom || px >= xmaxd - pad_right) { 2491 value = 0U; 2492 } else { 2493 fx = ((px - pad_left) * xscale) >> SCALE_FIXED_POINT; 2494 fy = ((py - pad_top) * yscale) >> SCALE_FIXED_POINT; 2495 if (fx < w && fy < h) { 2496 value = (unsigned) pixels[fy * w + fx]; 2497 } else { 2498 value = 0U; 2499 } 2500 } 2501 2502 sx = (int) px + (slant_dx * (int) py) / ROT_SHEAR_SCALE; 2503 rx = x_sign_x * sx + x_sign_y * (int) py; 2504 ry = y_sign_x * sx + y_sign_y * (int) py; 2505 ox = rx + (rot_shear_x * ry) / ROT_SHEAR_SCALE; 2506 oy = ry + (rot_shear_y * ox) / ROT_SHEAR_SCALE; 2507 ox += (rot_shear_x * oy) / ROT_SHEAR_SCALE; 2508 2509 draw_regis_pixel(context, 2510 (int) context->graphics_output_cursor_x + ox, 2511 (int) context->graphics_output_cursor_y + oy, 2512 value); 2513 } 2514 } 2515} 2516 2517static void 2518move_text(RegisGraphicsContext *context, int dx, int dy) 2519{ 2520 double total_rotation; 2521 int str_invert; 2522 int str_shear_x, str_shear_y; 2523 int ox, oy; 2524 2525 total_rotation = 2.0 * M_PI * 2526 context->current_text_controls->string_rotation / 360.0; 2527 while (total_rotation > 1.5 * M_PI) { 2528 total_rotation -= 2.0 * M_PI; 2529 } 2530 if (total_rotation > 0.5 * M_PI) { 2531 total_rotation -= M_PI; 2532 str_invert = -1; 2533 } else { 2534 str_invert = 1; 2535 } 2536 str_shear_x = (int) (ROT_SHEAR_SCALE * -tan(0.5 * -total_rotation)); 2537 str_shear_y = (int) (ROT_SHEAR_SCALE * sin(-total_rotation)); 2538 2539 total_rotation = 2.0 * M_PI * 2540 context->current_text_controls->character_rotation / 360.0; 2541 while (total_rotation > 1.5 * M_PI) { 2542 total_rotation -= 2.0 * M_PI; 2543 } 2544 2545 TRACE(("str_shear: %.5f, %.5f (sign=%d)\n", 2546 str_shear_x / (double) ROT_SHEAR_SCALE, 2547 str_shear_y / (double) ROT_SHEAR_SCALE, 2548 str_invert)); 2549 2550 ox = str_invert * dx + (str_shear_x * dy) / ROT_SHEAR_SCALE; 2551 oy = str_invert * dy + (str_shear_y * ox) / ROT_SHEAR_SCALE; 2552 ox += (str_shear_x * oy) / ROT_SHEAR_SCALE; 2553 2554 TRACE(("after pv output updating position %+d,%+d\n", ox, oy)); 2555 context->graphics_output_cursor_x += ox; 2556 context->graphics_output_cursor_y += oy; 2557 2558 return; 2559} 2560 2561#define UPSCALE_TEXT_DIMENSION(D) do { \ 2562 *(D) = (unsigned)((double)(*(D)) * M_SQRT2); \ 2563 } while (0) 2564 2565static void 2566draw_text(RegisGraphicsContext *context, char const *str) 2567{ 2568#ifndef ENABLE_DISTORTIONLESS_ROTATION 2569 RegisTextControls *old_text_controls = NULL; 2570 static RegisTextControls scratch_text_controls; 2571#endif 2572 double total_rotation; 2573 size_t ii; 2574 int str_invert; 2575 int str_shear_x, str_shear_y; 2576 int slant_dx; 2577 int chr_x_sign_x, chr_x_sign_y; 2578 int chr_y_sign_x, chr_y_sign_y; 2579 int chr_shear_x, chr_shear_y; 2580 int begin_x, begin_y; 2581 int rx, ry; 2582 int ox, oy; 2583 2584#ifdef DEBUG_ALPHABETS 2585 { 2586 unsigned n; 2587 2588 for (n = 0U; n < MAX_REGIS_ALPHABETS; n++) { 2589 printf("alphabet index %u\n", n); 2590 if (context->alphabets[n].alphabet_num != INVALID_ALPHABET_NUM) { 2591 printf(" alphabet_num=%u\n", context->alphabets[n].alphabet_num); 2592 printf(" pixw=%d\n", context->alphabets[n].pixw); 2593 printf(" pixh=%d\n", context->alphabets[n].pixh); 2594 printf(" name=\"%s\"\n", context->alphabets[n].name); 2595 printf(" use_font=%d\n", context->alphabets[n].use_font); 2596 printf(" fontname=\"%s\"\n", context->alphabets[n].fontname); 2597 printf(" bytes=%p\n", context->alphabets[n].bytes); 2598 } 2599 } 2600 } 2601#endif 2602 2603 if (context->current_text_controls->slant <= -75 || 2604 context->current_text_controls->slant >= +75) { 2605 TRACE(("ERROR: unsupported character slant angle %d\n", 2606 context->current_text_controls->slant)); 2607 return; 2608 } 2609 2610 /* FIXME: grab when first entering command */ 2611 begin_x = context->graphics_output_cursor_x; 2612 begin_y = context->graphics_output_cursor_y; 2613 2614#ifndef ENABLE_DISTORTIONLESS_ROTATION 2615 if (context->current_text_controls->character_rotation != 0 && 2616 context->current_text_controls->character_rotation != 90 && 2617 context->current_text_controls->character_rotation != 180 && 2618 context->current_text_controls->character_rotation != 270) { 2619 old_text_controls = context->current_text_controls; 2620 scratch_text_controls = *context->current_text_controls; 2621 UPSCALE_TEXT_DIMENSION(&scratch_text_controls.character_display_w); 2622 UPSCALE_TEXT_DIMENSION(&scratch_text_controls.character_display_h); 2623 /* FIXME: Not sure if this is really scaled. The increment seems to 2624 * _not_ be scaled. 2625 */ 2626 UPSCALE_TEXT_DIMENSION(&scratch_text_controls.character_unit_cell_w); 2627 UPSCALE_TEXT_DIMENSION(&scratch_text_controls.character_unit_cell_h); 2628 context->current_text_controls = &scratch_text_controls; 2629 TRACE(("scaled up text to %dx%d\n", 2630 scratch_text_controls.character_display_w, 2631 scratch_text_controls.character_display_h)); 2632 } 2633#endif 2634 2635 total_rotation = 2.0 * M_PI * 2636 context->current_text_controls->string_rotation / 360.0; 2637 while (total_rotation > 1.5 * M_PI) { 2638 total_rotation -= 2.0 * M_PI; 2639 } 2640 if (total_rotation > 0.5 * M_PI) { 2641 total_rotation -= M_PI; 2642 str_invert = -1; 2643 } else { 2644 str_invert = 1; 2645 } 2646 str_shear_x = (int) (ROT_SHEAR_SCALE * -tan(0.5 * -total_rotation)); 2647 str_shear_y = (int) (ROT_SHEAR_SCALE * sin(-total_rotation)); 2648 2649 total_rotation = 2.0 * M_PI * 2650 context->current_text_controls->character_rotation / 360.0; 2651 while (total_rotation > 1.5 * M_PI) { 2652 total_rotation -= 2.0 * M_PI; 2653 } 2654 if (total_rotation > 0.5 * M_PI) { 2655 total_rotation -= M_PI; 2656 chr_x_sign_x = -1; 2657 chr_x_sign_y = 0; 2658 chr_y_sign_x = 0; 2659 chr_y_sign_y = -1; 2660 } else { 2661 chr_x_sign_x = 1; 2662 chr_x_sign_y = 0; 2663 chr_y_sign_x = 0; 2664 chr_y_sign_y = 1; 2665 } 2666 chr_shear_x = (int) (ROT_SHEAR_SCALE * -tan(0.5 * -total_rotation)); 2667 chr_shear_y = (int) (ROT_SHEAR_SCALE * sin(-total_rotation)); 2668 2669 { 2670 const int slant = context->current_text_controls->slant; 2671 2672 TRACE(("float version: %.5f\n", tan(2.0 * M_PI * abs(slant) / 360.0))); 2673 /* The slant is negative for forward-leaning characters. */ 2674 if (slant > 0) { 2675 slant_dx = (int) +(tan(2.0 * M_PI * abs(slant) / 360.0) * ROT_SHEAR_SCALE); 2676 } else if (slant < 0) { 2677 slant_dx = (int) -(tan(2.0 * M_PI * abs(slant) / 360.0) * ROT_SHEAR_SCALE); 2678 } else { 2679 slant_dx = 0; 2680 } 2681 TRACE(("string rotation: %d\n", 2682 context->current_text_controls->string_rotation)); 2683 TRACE(("character rotation: %d\n", 2684 context->current_text_controls->character_rotation)); 2685 TRACE(("character slant: %d (%.5f pixels per line)\n", 2686 slant, slant_dx / (double) ROT_SHEAR_SCALE)); 2687 } 2688 2689 TRACE(("str_shear: %.5f, %.5f (sign=%d)\n", 2690 str_shear_x / (double) ROT_SHEAR_SCALE, 2691 str_shear_y / (double) ROT_SHEAR_SCALE, 2692 str_invert)); 2693 TRACE(("chr_shear: %.5f, %.5f (xsign=%d,%d, ysign=%d,%d)\n", 2694 chr_shear_x / (double) ROT_SHEAR_SCALE, 2695 chr_shear_y / (double) ROT_SHEAR_SCALE, 2696 chr_x_sign_x, chr_x_sign_y, 2697 chr_y_sign_x, chr_y_sign_y)); 2698 TRACE(("character_inc: %d,%d\n", 2699 context->current_text_controls->character_inc_x, context->current_text_controls->character_inc_y)); 2700 2701 rx = 0; 2702 ry = 0; 2703 for (ii = 0U; ii < strlen(str); ii++) { 2704 switch (str[ii]) { 2705 case '\r': 2706 rx = 0; 2707 break; 2708 case '\n': 2709 ry += (int) context->current_text_controls->character_display_h; 2710 break; 2711 case '\b': 2712 rx -= context->current_text_controls->character_inc_x; 2713 ry -= context->current_text_controls->character_inc_y; 2714 break; 2715 case '\t': 2716 rx += context->current_text_controls->character_inc_x; 2717 ry += context->current_text_controls->character_inc_y; 2718 break; 2719 default: 2720 ox = str_invert * rx + (str_shear_x * ry) / ROT_SHEAR_SCALE; 2721 oy = str_invert * ry + (str_shear_y * ox) / ROT_SHEAR_SCALE; 2722 ox += (str_shear_x * oy) / ROT_SHEAR_SCALE; 2723 TRACE(("during text output updating position to %d,%d + %+d,%+d for '%c'\n", 2724 begin_x, begin_y, ox, oy, str[ii])); 2725 context->graphics_output_cursor_x = begin_x + ox; 2726 context->graphics_output_cursor_y = begin_y + oy; 2727 draw_character(context, str[ii], slant_dx, 2728 chr_shear_x, chr_shear_y, 2729 chr_x_sign_x, chr_x_sign_y, 2730 chr_y_sign_x, chr_y_sign_y); 2731 rx += context->current_text_controls->character_inc_x; 2732 ry += context->current_text_controls->character_inc_y; 2733 } 2734 } 2735 2736 ox = str_invert * rx + (str_shear_x * ry) / ROT_SHEAR_SCALE; 2737 oy = str_invert * ry + (str_shear_y * ox) / ROT_SHEAR_SCALE; 2738 ox += (str_shear_x * oy) / ROT_SHEAR_SCALE; 2739 TRACE(("after text output updating position to %d,%d + %+d,%+d\n", 2740 begin_x, begin_y, ox, oy)); 2741 context->graphics_output_cursor_x = begin_x + ox; 2742 context->graphics_output_cursor_y = begin_y + oy; 2743 2744#ifndef ENABLE_DISTORTIONLESS_ROTATION 2745 if (context->current_text_controls->character_rotation != 0 && 2746 context->current_text_controls->character_rotation != 90 && 2747 context->current_text_controls->character_rotation != 180 && 2748 context->current_text_controls->character_rotation != 270) { 2749 context->current_text_controls = old_text_controls; 2750 } 2751#endif 2752 2753 context->destination_graphic->dirty = 1; 2754 return; 2755} 2756 2757/* 2758 * standard character cell sizes 2759 * number disp cell unit cell offset 2760 * S0 [ 9, 10] [ 8, disp_h] [disp_w, 0] 2761 * S1 [ 9, 20] [ 8, disp_h] [disp_w, 0] 2762 * S2 [ 18, 30] [ 16, disp_h] [disp_w, 0] 2763 * S3 [ 27, 45] [ 24, disp_h] [disp_w, 0] 2764 * S4 [ 36, 60] [ 32, disp_h] [disp_w, 0] 2765 * S5 [ 45, 75] [ 40, disp_h] [disp_w, 0] 2766 * S6 [ 54, 90] [ 48, disp_h] [disp_w, 0] 2767 * S7 [ 63,105] [ 56, disp_h] [disp_w, 0] 2768 * S8 [ 72,120] [ 64, disp_h] [disp_w, 0] 2769 * S9 [ 81,135] [ 72, disp_h] [disp_w, 0] 2770 * S10 [ 90,150] [ 80, disp_h] [disp_w, 0] 2771 * S11 [ 99,165] [ 88, disp_h] [disp_w, 0] 2772 * S12 [108,180] [ 96, disp_h] [disp_w, 0] 2773 * S13 [117,195] [104, disp_h] [disp_w, 0] 2774 * S14 [126,210] [112, disp_h] [disp_w, 0] 2775 * S15 [135,225] [120, disp_h] [disp_w, 0] 2776 * S16 [144,240] [128, disp_h] [disp_w, 0] 2777 */ 2778static int 2779get_standard_character_size(int standard, unsigned *disp_w, unsigned 2780 *disp_h, unsigned *unit_w, unsigned *unit_h, 2781 int *off_x, int *off_y) 2782{ 2783 switch (standard) { 2784 case 0: 2785 *disp_w = 9U; 2786 *disp_h = 10U; 2787 *unit_w = 8U; 2788 break; 2789 case 1: 2790 *disp_w = 9U; 2791 *disp_h = 20U; 2792 *unit_w = 8U; 2793 break; 2794 case 2: 2795 *disp_w = 18U; 2796 *disp_h = 30U; 2797 *unit_w = 16U; 2798 break; 2799 case 3: 2800 *disp_w = 27U; 2801 *disp_h = 45U; 2802 *unit_w = 24U; 2803 break; 2804 case 4: 2805 *disp_w = 36U; 2806 *disp_h = 60U; 2807 *unit_w = 32U; 2808 break; 2809 case 5: 2810 *disp_w = 45U; 2811 *disp_h = 75U; 2812 *unit_w = 40U; 2813 break; 2814 case 6: 2815 *disp_w = 54U; 2816 *disp_h = 90U; 2817 *unit_w = 48U; 2818 break; 2819 case 7: 2820 *disp_w = 63U; 2821 *disp_h = 105U; 2822 *unit_w = 56U; 2823 break; 2824 case 8: 2825 *disp_w = 72U; 2826 *disp_h = 120U; 2827 *unit_w = 64U; 2828 break; 2829 case 9: 2830 *disp_w = 81U; 2831 *disp_h = 135U; 2832 *unit_w = 72U; 2833 break; 2834 case 10: 2835 *disp_w = 90U; 2836 *disp_h = 150U; 2837 *unit_w = 80U; 2838 break; 2839 case 11: 2840 *disp_w = 99U; 2841 *disp_h = 165U; 2842 *unit_w = 88U; 2843 break; 2844 case 12: 2845 *disp_w = 108U; 2846 *disp_h = 180U; 2847 *unit_w = 96U; 2848 break; 2849 case 13: 2850 *disp_w = 117U; 2851 *disp_h = 195U; 2852 *unit_w = 104U; 2853 break; 2854 case 14: 2855 *disp_w = 126U; 2856 *disp_h = 210U; 2857 *unit_w = 112U; 2858 break; 2859 case 15: 2860 *disp_w = 135U; 2861 *disp_h = 225U; 2862 *unit_w = 120U; 2863 break; 2864 case 16: 2865 *disp_w = 144U; 2866 *disp_h = 240U; 2867 *unit_w = 128U; 2868 break; 2869 default: 2870 return 1; 2871 } 2872 *unit_h = *disp_h; 2873 *off_x = (int) *disp_w; 2874 *off_y = 0; 2875 2876 return 0; 2877} 2878 2879static void 2880init_fragment(RegisDataFragment *fragment, char const *str) 2881{ 2882 assert(fragment); 2883 assert(str); 2884 2885 fragment->start = str; 2886 fragment->len = (unsigned) strlen(str); 2887 fragment->pos = 0U; 2888} 2889 2890static void 2891copy_fragment(RegisDataFragment *dst, RegisDataFragment const *src) 2892{ 2893 assert(dst); 2894 assert(src); 2895 2896 dst->start = src->start; 2897 dst->len = src->len; 2898 dst->pos = src->pos; 2899} 2900 2901static char 2902peek_fragment(RegisDataFragment const *fragment) 2903{ 2904 assert(fragment); 2905 2906 if (fragment->pos < fragment->len) { 2907 return fragment->start[fragment->pos]; 2908 } 2909 return '\0'; 2910} 2911 2912static char 2913pop_fragment(RegisDataFragment *fragment) 2914{ 2915 assert(fragment); 2916 2917 if (fragment->pos < fragment->len) { 2918 return fragment->start[fragment->pos++]; 2919 } 2920 return '\0'; 2921} 2922 2923static char 2924get_fragment(RegisDataFragment const *fragment, unsigned pos) 2925{ 2926 assert(fragment); 2927 2928 if (fragment->pos + pos < fragment->len) { 2929 return fragment->start[fragment->pos + pos]; 2930 } 2931 return '\0'; 2932} 2933 2934#define fragment_length(f) (f)->len 2935 2936static unsigned 2937fragment_remaining(RegisDataFragment const *fragment) 2938{ 2939 assert(fragment); 2940 2941 if (fragment->pos > fragment->len) 2942 return 0U; 2943 return fragment->len - fragment->pos; 2944} 2945 2946static int 2947fragment_consumed(RegisDataFragment const *fragment) 2948{ 2949 assert(fragment); 2950 2951 return fragment->pos >= fragment->len; 2952} 2953 2954static void 2955fragment_to_string(RegisDataFragment const *fragment, char *out, 2956 unsigned outlen) 2957{ 2958 unsigned remaininglen; 2959 unsigned endpos; 2960 2961 assert(fragment); 2962 assert(out); 2963 2964 if (!outlen) 2965 return; 2966 remaininglen = fragment->len - fragment->pos; 2967 if (remaininglen < outlen - 1U) { 2968 endpos = remaininglen; 2969 } else { 2970 endpos = outlen - 1U; 2971 } 2972 strncpy(out, &fragment->start[fragment->pos], (size_t) endpos); 2973 out[endpos] = '\0'; 2974} 2975 2976#define MAX_FRAG 1024 2977static char const * 2978fragment_to_tempstr(RegisDataFragment const *fragment) 2979{ 2980 static char tempstr[MAX_FRAG]; 2981 2982 assert(fragment); 2983 2984 fragment_to_string(fragment, tempstr, MAX_FRAG); 2985 return tempstr; 2986} 2987 2988static int 2989skip_regis_whitespace(RegisDataFragment *input) 2990{ 2991 int skipped = 0; 2992 2993 assert(input); 2994 2995 while (!fragment_consumed(input)) { 2996 char ch = peek_fragment(input); 2997 if (ch != ',' && !IsSpace(ch)) { 2998 break; 2999 } 3000 if (ch == '\n') { 3001 TRACE(("end of input line\n\n")); 3002 } 3003 skipped = 1; 3004 pop_fragment(input); 3005 } 3006 3007 if (skipped) 3008 return 1; 3009 return 0; 3010} 3011 3012static int 3013extract_regis_extent(RegisDataFragment *input, RegisDataFragment *output) 3014{ 3015 char ch; 3016 3017 assert(input); 3018 assert(output); 3019 3020 output->start = &input->start[input->pos]; 3021 output->len = 0U; 3022 output->pos = 0U; 3023 3024 if (input->pos >= input->len) 3025 return 0; 3026 3027 ch = input->start[input->pos]; 3028 if (ch != '[') 3029 return 0; 3030 input->pos++; 3031 output->start++; 3032 3033 /* FIXME: truncate to 16 bit signed integers */ 3034 for (; input->pos < input->len; input->pos++, output->len++) { 3035 ch = input->start[input->pos]; 3036 if (ch == ';') { 3037 TRACE(("DATA_ERROR: end of input before closing bracket\n")); 3038 break; 3039 } 3040 if (ch == ']') 3041 break; 3042 } 3043 if (ch == ']') 3044 input->pos++; 3045 3046 return 1; 3047} 3048 3049static int 3050extract_regis_num(RegisDataFragment *input, RegisDataFragment *output) 3051{ 3052 char ch = 0; 3053 int has_digits = 0; 3054 3055 assert(input); 3056 assert(output); 3057 3058 output->start = &input->start[input->pos]; 3059 output->len = 0U; 3060 output->pos = 0U; 3061 3062 if (input->start[input->pos] == '-' || 3063 input->start[input->pos] == '+') { 3064 input->pos++; 3065 output->len++; 3066 } 3067 3068 for (; input->pos < input->len; input->pos++, output->len++) { 3069 ch = input->start[input->pos]; 3070 if (ch != '0' && ch != '1' && ch != '2' && ch != '3' && 3071 ch != '4' && ch != '5' && ch != '6' && ch != '7' && 3072 ch != '8' && ch != '9') { 3073 break; 3074 } 3075 has_digits = 1; 3076 } 3077 3078 /* FIXME: what degenerate forms should be accepted ("E10" "1E" "1e" "1." "1ee10")? */ 3079 /* FIXME: the terminal is said to support "floating point values", truncating to int... what do these look like? */ 3080 if (has_digits && ch == 'E') { 3081 input->pos++; 3082 output->len++; 3083 for (; input->pos < input->len; input->pos++, output->len++) { 3084 ch = input->start[input->pos]; 3085 if (ch != '0' && ch != '1' && ch != '2' && ch != '3' && 3086 ch != '4' && ch != '5' && ch != '6' && ch != '7' && 3087 ch != '8' && ch != '9') { 3088 break; 3089 } 3090 } 3091 } 3092 3093 return has_digits; 3094} 3095 3096static int 3097extract_regis_pixelvector(RegisDataFragment *input, RegisDataFragment *output) 3098{ 3099 char ch; 3100 int has_digits; 3101 3102 assert(input); 3103 assert(output); 3104 3105 output->start = &input->start[input->pos]; 3106 output->len = 0U; 3107 output->pos = 0U; 3108 3109 if (input->pos < input->len) { 3110 ch = input->start[input->pos]; 3111 if (ch == '+' || ch == '-') { 3112 input->pos++; 3113 output->len++; 3114 } 3115 } 3116 3117 has_digits = 0; 3118 for (; input->pos < input->len; input->pos++, output->len++) { 3119 ch = input->start[input->pos]; 3120 if (ch != '0' && ch != '1' && ch != '2' && ch != '3' && 3121 ch != '4' && ch != '5' && ch != '6' && ch != '7') { 3122 break; 3123 } 3124 has_digits = 1; 3125 } 3126 3127 return has_digits; 3128} 3129 3130static int 3131extract_regis_command(RegisDataFragment *input, char *command) 3132{ 3133 char ch; 3134 3135 assert(input); 3136 assert(command); 3137 3138 if (input->pos >= input->len) 3139 return 0; 3140 3141 ch = input->start[input->pos]; 3142 if (ch == '\0' || ch == ';') { 3143 return 0; 3144 } 3145 if (!islower(CharOf(ch)) && !isupper(CharOf(ch)) && ch != '@') { 3146 return 0; 3147 } 3148 *command = ch; 3149 input->pos++; 3150 3151 return 1; 3152} 3153 3154static int 3155extract_regis_string(RegisDataFragment *input, char *out, unsigned maxlen) 3156{ 3157 char open_quote_ch; 3158 char ch; 3159 unsigned outlen; 3160 3161 assert(input); 3162 assert(out); 3163 assert(maxlen > 0U); 3164 3165 if (input->pos >= input->len) 3166 return 0; 3167 3168 ch = peek_fragment(input); 3169 if (ch != '\'' && ch != '"') 3170 return 0; 3171 open_quote_ch = ch; 3172 outlen = 0U; 3173 pop_fragment(input); 3174 3175 ch = '\0'; 3176 while (!fragment_consumed(input)) { 3177 char prev_ch = ch; 3178 ch = peek_fragment(input); 3179 /* ';' (resync) and '@' (macrograph) are not recognized in strings */ 3180 if (prev_ch == open_quote_ch) { 3181 if (ch == open_quote_ch) { 3182 if (outlen < maxlen) { 3183 out[outlen] = ch; 3184 } 3185 outlen++; 3186 pop_fragment(input); 3187 ch = '\0'; 3188 continue; 3189 } 3190 if (outlen < maxlen) 3191 out[outlen] = '\0'; 3192 else 3193 out[maxlen] = '\0'; 3194 return 1; 3195 } 3196 if (ch == '\0') 3197 break; 3198 if (ch != open_quote_ch) { 3199 if (outlen < maxlen) 3200 out[outlen] = ch; 3201 outlen++; 3202 } 3203 pop_fragment(input); 3204 } 3205 if (ch == open_quote_ch) { 3206 pop_fragment(input); 3207 if (outlen < maxlen) 3208 out[outlen] = '\0'; 3209 else 3210 out[maxlen] = '\0'; 3211 return 1; 3212 } 3213 /* FIXME: handle multiple strings concatenated with commas */ 3214 3215 TRACE(("DATA_ERROR: end of input before closing quote\n")); 3216 return 0; 3217} 3218 3219static int 3220extract_regis_parenthesized_data(RegisDataFragment *input, 3221 RegisDataFragment *output) 3222{ 3223 char ch; 3224 char open_quote_ch; 3225 int nesting; 3226 3227 assert(input); 3228 assert(output); 3229 3230 output->start = &input->start[input->pos]; 3231 output->len = 0U; 3232 output->pos = 0U; 3233 3234 if (input->pos >= input->len) 3235 return 0; 3236 3237 ch = input->start[input->pos]; 3238 if (ch != '(') 3239 return 0; 3240 input->pos++; 3241 output->start++; 3242 nesting = 1; 3243 open_quote_ch = '\0'; 3244 3245 ch = '\0'; 3246 for (; input->pos < input->len; input->pos++, output->len++) { 3247 char prev_ch = ch; 3248 ch = input->start[input->pos]; 3249 if (ch == '\'' || ch == '"') { 3250 if (open_quote_ch == '\0') { 3251 open_quote_ch = ch; 3252 } else { 3253 if (ch == prev_ch && prev_ch == open_quote_ch) { 3254 ch = '\0'; 3255 } else if (ch == open_quote_ch) { 3256 open_quote_ch = '\0'; 3257 } 3258 } 3259 continue; 3260 } 3261 if (open_quote_ch != '\0') 3262 continue; 3263 3264 if (ch == ';') { 3265 TRACE(("leaving parenthesized data nested %d levels deep due to command termination character\n", 3266 nesting)); 3267 break; 3268 } 3269 if (ch == '(') 3270 nesting++; 3271 if (ch == ')') { 3272 nesting--; 3273 if (nesting == 0) { 3274 input->pos++; 3275 return 1; 3276 } 3277 } 3278 } 3279 3280 TRACE(("DATA_ERROR: end of input before closing paren (%d levels deep)\n", 3281 nesting)); 3282 return 0; 3283} 3284 3285static int 3286extract_regis_option(RegisDataFragment *input, 3287 char *option, 3288 RegisDataFragment *output) 3289{ 3290 char ch; 3291 int paren_level, bracket_level; 3292 char open_quote_ch; 3293 3294 assert(input); 3295 assert(option); 3296 assert(output); 3297 3298 /* LETTER suboptions* value? */ 3299 /* 3300 * FIXME: what are the rules for using separate parens vs. sharing between 3301 * options? 3302 */ 3303 3304 output->start = &input->start[input->pos]; 3305 output->len = 0U; 3306 output->pos = 0U; 3307 3308 if (input->pos >= input->len) { 3309 return 0; 3310 } 3311 3312 ch = input->start[input->pos]; 3313 /* FIXME: are options always letters or are some special characters ok? */ 3314 if (ch == ';' || ch == ',' || 3315 ch == '(' || ch == ')' || 3316 ch == '[' || ch == ']' || 3317 ch == '"' || ch == '\'' || 3318 isdigit(CharOf(ch))) { 3319 return 0; 3320 } 3321 *option = ch; 3322 input->pos++; 3323 output->start++; 3324 paren_level = 0; 3325 bracket_level = 0; 3326 3327 open_quote_ch = '\0'; 3328 for (; input->pos < input->len; input->pos++, output->len++) { 3329 ch = input->start[input->pos]; 3330 TRACE(("looking at char '%c' in option '%c'\n", ch, *option)); 3331 /* FIXME: any special rules for commas? */ 3332 /* FIXME: handle escaped quotes */ 3333 if (ch == '\'' || ch == '"') { 3334 if (open_quote_ch == ch) { 3335 open_quote_ch = '\0'; 3336 } else { 3337 open_quote_ch = ch; 3338 } 3339 continue; 3340 } 3341 if (open_quote_ch != '\0') 3342 continue; 3343 if (ch == '(') { 3344 paren_level++; 3345 } 3346 if (ch == ')') { 3347 paren_level--; 3348 if (paren_level < 0) { 3349 TRACE(("DATA_ERROR: found ReGIS option has value with too many close parens \"%c\"\n", 3350 *option)); 3351 return 0; 3352 } 3353 } 3354 if (ch == '[') { 3355 bracket_level++; 3356 } 3357 if (ch == ']') { 3358 bracket_level--; 3359 if (bracket_level < 0) { 3360 TRACE(("DATA_ERROR: found ReGIS option has value with too many close brackets \"%c\"\n", 3361 *option)); 3362 return 0; 3363 } 3364 } 3365 if (paren_level == 0 && bracket_level == 0) { 3366 /* 3367 * Top-level commas indicate the end of this option and the start of 3368 * another. 3369 */ 3370 if (ch == ',') 3371 break; 3372 /* 3373 * Top-level command/option/suboption names also indicate the end of 3374 * this option. "E" is valid as the exponent indicator in a numeric 3375 * parameter. 3376 */ 3377 if (ch != 'E' && ch != 'e' && 3378 ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z'))) 3379 break; 3380 } 3381 if (ch == ';') 3382 break; 3383 } 3384 if (paren_level != 0) { 3385 TRACE(("DATA_ERROR: mismatched parens in argument to ReGIS option \"%c\"\n", 3386 *option)); 3387 return 0; 3388 } 3389 if (bracket_level != 0) { 3390 TRACE(("DATA_ERROR: mismatched brackets in argument to ReGIS option \"%c\"\n", 3391 *option)); 3392 return 0; 3393 } 3394 3395 TRACE(("found ReGIS option and value \"%c\" \"%s\"\n", 3396 *option, 3397 fragment_to_tempstr(output))); 3398 return 1; 3399} 3400 3401static int 3402regis_num_to_int(RegisDataFragment const *input, int *out) 3403{ 3404 char ch; 3405 3406 assert(input); 3407 assert(out); 3408 3409 /* FIXME: handle exponential notation and rounding */ 3410 /* FIXME: check for junk after the number */ 3411 ch = peek_fragment(input); 3412 if (!isdigit(CharOf(ch)) && 3413 ch != '+' && 3414 ch != '-') { 3415 *out = 0; 3416 return 0; 3417 } 3418 3419 TRACE(("converting \"%s\" to an int\n", fragment_to_tempstr(input))); 3420 *out = atoi(fragment_to_tempstr(input)); 3421 return 1; 3422} 3423 3424static int 3425load_regis_colorspec(RegisGraphicsContext const *context, 3426 RegisDataFragment const *input, 3427 short *r_out, short *g_out, short *b_out) 3428{ 3429 RegisDataFragment colorspec; 3430 short r = -1, g = -1, b = -1; 3431 short l = -1; 3432 int simple; 3433 3434 assert(context); 3435 assert(input); 3436 assert(r_out); 3437 assert(g_out); 3438 assert(b_out); 3439 3440 copy_fragment(&colorspec, input); 3441 TRACE(("colorspec option: \"%s\"\n", fragment_to_tempstr(&colorspec))); 3442 3443 skip_regis_whitespace(&colorspec); 3444 simple = 0; 3445 if (fragment_remaining(&colorspec) == 1U) { 3446 simple = 1; 3447 } else if (fragment_remaining(&colorspec) > 1U) { 3448 char after = get_fragment(&colorspec, 1U); 3449 if (IsSpace(after)) 3450 simple = 1; 3451 } 3452 if (simple) { 3453 char ch = pop_fragment(&colorspec); 3454 3455 TRACE(("got ReGIS RGB colorspec pattern '%c' with arguments: \"%s\"\n", 3456 ch, fragment_to_tempstr(&colorspec))); 3457 switch (ch) { 3458 case 'D': 3459 case 'd': 3460 r = 0; 3461 g = 0; 3462 b = 0; 3463 l = 0; 3464 break; 3465 case 'R': 3466 case 'r': 3467 r = 100; 3468 g = 0; 3469 b = 0; 3470 l = 46; 3471 break; 3472 case 'G': 3473 case 'g': 3474 r = 0; 3475 g = 100; 3476 b = 0; 3477 l = 50; 3478 break; 3479 case 'B': 3480 case 'b': 3481 r = 0; 3482 g = 0; 3483 b = 100; 3484 l = 50; 3485 break; 3486 case 'C': 3487 case 'c': 3488 r = 0; 3489 g = 100; 3490 b = 100; 3491 l = 50; 3492 break; 3493 case 'Y': 3494 case 'y': 3495 r = 100; 3496 g = 100; 3497 b = 0; 3498 l = 50; 3499 break; 3500 case 'M': 3501 case 'm': 3502 r = 100; 3503 g = 0; 3504 b = 100; 3505 l = 50; 3506 break; 3507 case 'W': 3508 case 'w': 3509 r = 100; 3510 g = 100; 3511 b = 100; 3512 l = 100; 3513 break; 3514 default: 3515 TRACE(("DATA_ERROR: unknown RGB color name: \"%c\"\n", ch)); 3516 return 0; 3517 } 3518 } else { 3519 RegisDataFragment num; 3520 int max, val; 3521 char comp; 3522 short h = -1; 3523 short s = -1; 3524 3525 while (!fragment_consumed(&colorspec)) { 3526 if (skip_regis_whitespace(&colorspec)) 3527 continue; 3528 3529 comp = pop_fragment(&colorspec); 3530 switch (comp) { 3531 case ',': 3532 /* not sure if this is valid, but it is easy to handle */ 3533 continue; 3534 case 'H': 3535 case 'h': 3536 max = 360; 3537 comp = 'H'; 3538 break; 3539 case 'L': 3540 case 'l': 3541 max = 100; 3542 comp = 'L'; 3543 break; 3544 case 'S': 3545 case 's': 3546 max = 100; 3547 comp = 'S'; 3548 break; 3549#ifdef ENABLE_RGB_COLORSPECS 3550 case 'R': /* RLogin extension */ 3551 case 'r': 3552 max = 100; 3553 comp = 'R'; 3554 break; 3555 case 'G': /* RLogin extension */ 3556 case 'g': 3557 max = 100; 3558 comp = 'G'; 3559 break; 3560 case 'B': /* RLogin extension */ 3561 case 'b': 3562 max = 100; 3563 comp = 'B'; 3564 break; 3565#endif 3566 default: 3567 TRACE(("DATA_ERROR: unrecognized component in colorspec: '%c'\n", 3568 comp)); 3569 return 0; 3570 } 3571 3572 skip_regis_whitespace(&colorspec); 3573 if (!extract_regis_num(&colorspec, &num)) { 3574 TRACE(("DATA_ERROR: expected int after '%c' component in colorspec: \"%s\"\n", 3575 comp, fragment_to_tempstr(&colorspec))); 3576 return 0; 3577 } 3578 if (!regis_num_to_int(&num, &val)) { 3579 TRACE(("DATA_ERROR: component value %s is not a number\n", 3580 fragment_to_tempstr(&num))); 3581 return 0; 3582 } 3583 /* FIXME: error, truncate, wrap, ...? */ 3584 if (val < 0 || val > max) { 3585 TRACE(("DATA_ERROR: component value %d out of range\n", val)); 3586 return 0; 3587 } 3588 3589 switch (comp) { 3590 case 'H': 3591 h = (short) val; 3592 break; 3593 case 'L': 3594 l = (short) val; 3595 break; 3596 case 'S': 3597 s = (short) val; 3598 break; 3599 case 'R': 3600 r = (short) val; 3601 break; 3602 case 'G': 3603 g = (short) val; 3604 break; 3605 case 'B': 3606 b = (short) val; 3607 break; 3608 } 3609 } 3610 3611 if (h >= 0 && l >= 0 && s >= 0 && r < 0 && g < 0 && b < 0) { 3612 TRACE(("found HLS colorspec to be converted: %hd,%hd,%hd\n", 3613 h, l, s)); 3614 hls2rgb(h, l, s, &r, &g, &b); 3615 TRACE(("converted to RGB: %hd,%hd,%hd\n", r, g, b)); 3616 } else if (h < 0 && l < 0 && s < 0 && r >= 0 && g >= 0 && b >= 0) { 3617 TRACE(("found RGB colorspec: %hd,%hd,%hd\n", r, g, b)); 3618 l = (short) ((MIN3(r, g, b) + MAX3(r, g, b)) / 2); 3619 TRACE(("calculated L: %d\n", l)); 3620 } else if (h < 0 && l >= 0 && s < 0 && r < 0 && g < 0 && b < 0) { 3621 TRACE(("found L colorspec to be converted: %hd,%hd,%hd\n", 3622 h, l, s)); 3623 hls2rgb(0, l, 0, &r, &g, &b); 3624 TRACE(("converted to RGB: %hd,%hd,%hd\n", r, g, b)); 3625 } else { 3626 TRACE(("DATA_ERROR: unrecognized colorspec format\n")); 3627 return 0; 3628 } 3629 } 3630 3631 /* 3632 * The VT240 and VT330 models convert to the closest grayscale value. 3633 */ 3634 if (context->graphics_termid == 240 || context->graphics_termid == 330) { 3635 hls2rgb(0, l, 0, &r, &g, &b); 3636 TRACE(("converted to grayscale: %hd,%hd,%hd\n", r, g, b)); 3637 } 3638 3639 *r_out = r; 3640 *g_out = g; 3641 *b_out = b; 3642 3643 skip_regis_whitespace(&colorspec); 3644 if (!fragment_consumed(&colorspec)) { 3645 char skip; 3646 3647 skip = pop_fragment(&colorspec); 3648 (void) skip; /* variable needed only if tracing */ 3649 TRACE(("DATA_ERROR: ignoring unexpected character in ReGIS colorspec \"%c\"\n", 3650 skip)); 3651 } 3652 3653 return 1; 3654} 3655 3656static int 3657load_regis_regnum_or_colorspec(RegisGraphicsContext const *context, 3658 RegisDataFragment const *input, 3659 RegisterNum *out) 3660{ 3661 int val; 3662 RegisDataFragment colorspec; 3663 RegisDataFragment num; 3664 RegisDataFragment coloroption; 3665 3666 copy_fragment(&colorspec, input); 3667 TRACE(("looking at colorspec pattern: \"%s\"\n", 3668 fragment_to_tempstr(&colorspec))); 3669 3670 skip_regis_whitespace(&colorspec); 3671 3672 if (extract_regis_num(&colorspec, &num)) { 3673 if (!regis_num_to_int(&num, &val)) { 3674 TRACE(("DATA_ERROR: colorspec value %s is not a valid register\n", 3675 fragment_to_tempstr(&num))); 3676 return 0; 3677 } 3678 if (val < 0) { 3679 /* FIXME: error, truncate, wrap, ...? */ 3680 TRACE(("DATA_ERROR: ignoring negative colorspec value: %d\n", val)); 3681 return 0; 3682 } 3683 if (val >= (int) context->destination_graphic->valid_registers) { 3684 /* FIXME: error, truncate, wrap, ...? */ 3685 TRACE(("DATA_ERROR: colorspec value %d is too big; wrapping\n", 3686 val)); 3687 val %= (int) context->destination_graphic->valid_registers; 3688 } 3689 3690 TRACE(("colorspec contains index for register %u\n", val)); 3691 *out = (RegisterNum) val; 3692 3693 skip_regis_whitespace(&colorspec); 3694 if (!fragment_consumed(&colorspec)) { 3695 char skip; 3696 3697 skip = pop_fragment(&colorspec); 3698 (void) skip; /* variable needed only if tracing */ 3699 TRACE(("DATA_ERROR: unexpected character after register \"%c\"\n", 3700 skip)); 3701 return 0; 3702 } 3703 3704 return 1; 3705 } 3706 3707 if (extract_regis_parenthesized_data(&colorspec, &coloroption)) { 3708 short r, g, b; 3709 3710 if (!load_regis_colorspec(context, &coloroption, &r, &g, &b)) { 3711 TRACE(("unable to parse colorspec\n")); 3712 return 0; 3713 } 3714 3715 *out = find_color_register(context->destination_graphic->color_registers, 3716 r, g, b); 3717 TRACE(("colorspec maps to closest register %u\n", *out)); 3718 3719 return 1; 3720 } 3721 3722 TRACE(("expected register number or colorspec, but found: \"%s\"\n", 3723 fragment_to_tempstr(&colorspec))); 3724 return 0; 3725} 3726 3727static int 3728to_scaled_int(char const *num, int scale, int *value) 3729{ 3730 unsigned long whole, frac; 3731 char *end; 3732 3733 /* FIXME: handle whitespace? how about trailing junk? */ 3734 whole = strtoul(num, &end, 10); 3735 if (end[0] == '.') { 3736 char temp[5] = "0000"; 3737 3738 if (end[1] != '\0') { 3739 temp[0] = end[1]; 3740 if (end[2] != '\0') { 3741 temp[1] = end[2]; 3742 if (end[3] != '\0') { 3743 temp[2] = end[3]; 3744 if (end[4] != '\0') { 3745 temp[3] = end[4]; 3746 } 3747 } 3748 } 3749 } 3750 frac = strtoul(temp, NULL, 10); 3751 } else if (end[0] == '\0' || end[0] == ',' || IsSpace(end[0])) { 3752 frac = 0; 3753 } else { 3754 TRACE(("unexpected character %c in number %s\n", end[0], num)); 3755 return 0; 3756 } 3757 3758 *value = (int) (whole * (unsigned) scale + 3759 (frac * (unsigned) scale) / 10000); 3760 3761 return 1; 3762} 3763 3764static int 3765load_regis_raw_extent(char const *extent, int *relx, int *rely, 3766 int *xloc, int *yloc, int scale) 3767{ 3768 int xsign, ysign; 3769 char const *xpart; 3770 char const *ypart; 3771 3772 xpart = extent; 3773 if ((ypart = strchr(extent, ','))) { 3774 ypart++; 3775 } else { 3776 ypart = ""; 3777 } 3778 3779 while (IsSpace(xpart[0])) 3780 xpart++; 3781 while (IsSpace(ypart[0])) 3782 ypart++; 3783 3784 if (xpart[0] == '-') { 3785 xsign = -1; 3786 xpart++; 3787 } else if (xpart[0] == '+') { 3788 xsign = +1; 3789 xpart++; 3790 } else { 3791 xsign = 0; 3792 } 3793 if (ypart[0] == '-') { 3794 ysign = -1; 3795 ypart++; 3796 } else if (ypart[0] == '+') { 3797 ysign = +1; 3798 ypart++; 3799 } else { 3800 ysign = 0; 3801 } 3802 3803 if (xpart[0] == '\0' || xpart[0] == ',') { 3804 *relx = 1; 3805 *xloc = 0; 3806 } else if (xsign == 0) { 3807 int val; 3808 3809 if (!to_scaled_int(xpart, scale, &val)) 3810 return 0; 3811 *relx = 0; 3812 *xloc = val; 3813 } else { 3814 int val; 3815 3816 if (!to_scaled_int(xpart, scale, &val)) 3817 return 0; 3818 *relx = 1; 3819 *xloc = xsign * val; 3820 } 3821 if (ypart[0] == '\0') { 3822 *rely = 1; 3823 *yloc = 0; 3824 } else if (ysign == 0) { 3825 int val; 3826 3827 if (!to_scaled_int(ypart, scale, &val)) 3828 return 0; 3829 *rely = 0; 3830 *yloc = val; 3831 } else { 3832 int val; 3833 3834 if (!to_scaled_int(ypart, scale, &val)) 3835 return 0; 3836 *rely = 1; 3837 *yloc = ysign * val; 3838 } 3839 3840 return 1; 3841} 3842 3843static int 3844load_regis_mult_extent(char const *extent, int *w, int *h) 3845{ 3846 int relx, rely; 3847 int px, py; 3848 3849 if (!load_regis_raw_extent(extent, &relx, &rely, &px, &py, 1)) { 3850 TRACE(("invalid coordinates in extent %s\n", extent)); 3851 return 0; 3852 } 3853 if (relx | rely) { 3854 TRACE(("invalid relative value in multiplier extent %s\n", extent)); 3855 return 0; 3856 } 3857 3858 *w = px; 3859 *h = py; 3860 3861 return 1; 3862} 3863 3864static int 3865load_regis_pixel_extent(char const *extent, int origx, int origy, 3866 int *xloc, int *yloc) 3867{ 3868 int relx, rely; 3869 int px, py; 3870 3871 if (!load_regis_raw_extent(extent, &relx, &rely, &px, &py, 1)) { 3872 TRACE(("invalid coordinates in extent %s\n", extent)); 3873 return 0; 3874 } 3875 3876 *xloc = px; 3877 *yloc = py; 3878 3879 if (relx) 3880 *xloc += origx; 3881 if (rely) 3882 *yloc += origy; 3883 3884 return 1; 3885} 3886 3887#define COORD_SCALE 1000 3888 3889static int 3890load_regis_coord_extent(RegisGraphicsContext const *context, char const *extent, 3891 int origx, int origy, int *xloc, int *yloc) 3892{ 3893 int relx, rely; 3894 int ux, uy; 3895 3896 if (!load_regis_raw_extent(extent, &relx, &rely, &ux, &uy, COORD_SCALE)) { 3897 TRACE(("invalid coordinates in extent %s\n", extent)); 3898 return 0; 3899 } 3900 3901 if (relx) { 3902 const int px = SCALE_XCOORD(context, ux, COORD_SCALE); 3903 TRACE(("converted relative user X coord %.03f to relative pixel X coord %d (width=%d xoff=%d xdiv=%d)\n", 3904 ux / (double) COORD_SCALE, px, context->width, 3905 context->x_off, context->x_div)); 3906 *xloc = origx + px; 3907 } else { 3908 const int px = TRANSLATE_XCOORD(context, ux, COORD_SCALE); 3909 TRACE(("converted absolute user X coord %.03f to absolute pixel X coord %d\n", 3910 ux / (double) COORD_SCALE, px)); 3911 *xloc = px; 3912 } 3913 if (rely) { 3914 const int py = SCALE_YCOORD(context, uy, COORD_SCALE); 3915 TRACE(("converted relative user Y coord %.03f to relative pixel Y coord %d (height=%d yoff=%d ydiv=%d)\n", 3916 uy / (double) COORD_SCALE, py, context->height, 3917 context->y_off, context->y_div)); 3918 *yloc = origy + py; 3919 } else { 3920 const int py = TRANSLATE_YCOORD(context, uy, COORD_SCALE); 3921 TRACE(("converted absolute user Y coord %.03f to absolute pixel Y coord %d\n", 3922 uy / (double) COORD_SCALE, py)); 3923 *yloc = py; 3924 } 3925 3926 return 1; 3927} 3928 3929static int 3930load_regis_raw_pixelvector_digit(char const *pixelvector, 3931 unsigned *offset, 3932 int *dx, int *dy, int mul) 3933{ 3934 switch (pixelvector[*offset]) { 3935 case '0': 3936 *dx += mul; 3937 break; 3938 case '1': 3939 *dx += mul; 3940 *dy -= mul; 3941 break; 3942 case '2': 3943 *dy -= mul; 3944 break; 3945 case '3': 3946 *dx -= mul; 3947 *dy -= mul; 3948 break; 3949 case '4': 3950 *dx -= mul; 3951 break; 3952 case '5': 3953 *dx -= mul; 3954 *dy += mul; 3955 break; 3956 case '6': 3957 *dy += mul; 3958 break; 3959 case '7': 3960 *dx += mul; 3961 *dy += mul; 3962 break; 3963 default: 3964 return 0; 3965 } 3966 3967 (*offset)++; 3968 return 1; 3969} 3970 3971static int 3972load_regis_pixel_pixelvector(char const *pixelvector, 3973 int mul, 3974 int origx, int origy, 3975 int *xloc, int *yloc) 3976{ 3977 int found = 0; 3978 int px = 0, py = 0; 3979 unsigned offset = 0U; 3980 while (load_regis_raw_pixelvector_digit(pixelvector, &offset, 3981 &px, &py, 3982 mul)) 3983 found = 1; 3984 if (pixelvector[offset] != '\0') { 3985 TRACE(("DATA_ERROR: ignoring unknown pixel vector digits: \"%s\"\n", 3986 &pixelvector[offset])); 3987 } 3988 3989 *xloc = origx + px; 3990 *yloc = origy + py; 3991 3992 return found; 3993} 3994 3995static int 3996load_regis_coord_pixelvector(RegisGraphicsContext const *context, 3997 char const *pixelvector, 3998 int origx, int origy, 3999 int *xloc, int *yloc) 4000{ 4001 const int mul = (int) (context->temporary_write_controls.pv_multiplier 4002 * COORD_SCALE); 4003 int found = 0; 4004 int ux = 0, uy = 0; 4005 unsigned offset = 0U; 4006 4007 while (load_regis_raw_pixelvector_digit(pixelvector, &offset, 4008 &ux, &uy, 4009 mul)) 4010 found = 1; 4011 if (pixelvector[offset] != '\0') { 4012 TRACE(("DATA_ERROR: ignoring unknown pixel vector digits: \"%s\"\n", 4013 &pixelvector[offset])); 4014 } { 4015 const int px = SCALE_XCOORD(context, ux, COORD_SCALE); 4016 const int py = SCALE_YCOORD(context, uy, COORD_SCALE); 4017 4018 TRACE(("converted relative X coord %.03f to relative pixel X coord %d (width=%d xoff=%d xdiv=%d)\n", 4019 ux / (double) COORD_SCALE, px, context->width, 4020 context->x_off, context->x_div)); 4021 *xloc = origx + px; 4022 4023 TRACE(("converted relative Y coord %.03f to relative pixel Y coord %d (height=%d yoff=%d ydiv=%d)\n", 4024 uy / (double) COORD_SCALE, py, context->height, 4025 context->y_off, context->y_div)); 4026 *yloc = origy + py; 4027 } 4028 4029 return found; 4030} 4031 4032static int 4033load_regis_coord_pixelvector_step(RegisGraphicsContext const *context, 4034 char const *pixelvector, 4035 unsigned *offset, 4036 int origx, int origy, 4037 int *xloc, int *yloc) 4038{ 4039 const int mul = (int) (context->temporary_write_controls.pv_multiplier 4040 * COORD_SCALE); 4041 int found = 0; 4042 int ux = 0, uy = 0; 4043 if (load_regis_raw_pixelvector_digit(pixelvector, offset, &ux, &uy, mul)) 4044 found = 1; 4045 if (!found && pixelvector[*offset] != '\0') { 4046 TRACE(("DATA_ERROR: ignoring unknown pixel vector digits: \"%s\"\n", 4047 &pixelvector[*offset])); 4048 } { 4049 const int px = SCALE_XCOORD(context, ux, COORD_SCALE); 4050 const int py = SCALE_YCOORD(context, uy, COORD_SCALE); 4051 4052 TRACE(("converted relative X coord %.03f to relative pixel X coord %d (width=%d xoff=%d xdiv=%d)\n", 4053 ux / (double) COORD_SCALE, px, context->width, 4054 context->x_off, context->x_div)); 4055 *xloc = origx + px; 4056 4057 TRACE(("converted relative Y coord %.03f to relative pixel Y coord %d (height=%d yoff=%d ydiv=%d)\n", 4058 uy / (double) COORD_SCALE, py, context->height, 4059 context->y_off, context->y_div)); 4060 *yloc = origy + py; 4061 } 4062 4063 return found; 4064} 4065 4066static int 4067load_regis_write_control(RegisParseState *state, 4068 RegisGraphicsContext const *context, 4069 int cur_x, int cur_y, 4070 int option, 4071 RegisDataFragment *arg, 4072 RegisWriteControls *out) 4073{ 4074 TRACE(("checking write control option \"%c\" with arg \"%s\"\n", 4075 option, fragment_to_tempstr(arg))); 4076 switch (option) { 4077 case 'A': 4078 case 'a': 4079 TRACE(("write control alternate display method \"%s\"\n", 4080 fragment_to_tempstr(arg))); 4081 { 4082 int val; 4083 if (!regis_num_to_int(arg, &val) || val < 0 || val >= 1) { 4084 TRACE(("DATA_ERROR: interpreting out of range value as 0 FIXME\n")); 4085 break; 4086 } 4087 if (val == 1) { 4088 TRACE(("ERROR: blink display method not supported FIXME\n")); 4089 } 4090 } 4091 break; 4092 case 'C': 4093 case 'c': 4094 TRACE(("write control compliment writing mode \"%s\"\n", 4095 fragment_to_tempstr(arg))); 4096 out->write_style = WRITE_STYLE_COMPLEMENT; 4097 break; 4098 case 'E': 4099 case 'e': 4100 TRACE(("write control erase writing mode \"%s\"\n", 4101 fragment_to_tempstr(arg))); 4102 out->write_style = WRITE_STYLE_ERASE; 4103 break; 4104 case 'F': 4105 case 'f': 4106 TRACE(("write control plane write mask \"%s\"\n", 4107 fragment_to_tempstr(arg))); 4108 { 4109 int val; 4110 if (!regis_num_to_int(arg, &val) || 4111 val < 0 || val >= (int) context->destination_graphic->valid_registers) { 4112 TRACE(("DATA_ERROR: interpreting out of range value as 0 FIXME\n")); 4113 out->plane_mask = 0U; 4114 } else { 4115 out->plane_mask = (unsigned) val; 4116 } 4117 } 4118 break; 4119 case 'I': 4120 case 'i': 4121 TRACE(("write control foreground color \"%s\"\n", 4122 fragment_to_tempstr(arg))); 4123 if (!load_regis_regnum_or_colorspec(context, arg, &out->foreground)) { 4124 TRACE(("DATA_ERROR: write control foreground color specifier not recognized: \"%s\"\n", 4125 fragment_to_tempstr(arg))); 4126 return 0; 4127 } 4128 break; 4129 case 'L': 4130 case 'l': 4131 TRACE(("write control line width \"%s\" (FIXME: currently ignored)\n", 4132 fragment_to_tempstr(arg))); 4133 { 4134 int val; 4135 if (!regis_num_to_int(arg, &val) || 4136 val < 0 || val >= (int) 9) { 4137 TRACE(("interpreting out of range value as 1 FIXME\n")); 4138 out->line_width = 1U; 4139 } else { 4140 out->line_width = (unsigned) val; 4141 } 4142 } 4143 break; 4144 case 'M': 4145 case 'm': 4146 TRACE(("write control found pixel multiplication factor \"%s\"\n", 4147 fragment_to_tempstr(arg))); 4148 { 4149 int val; 4150 if (!regis_num_to_int(arg, &val) || val <= 0) { 4151 TRACE(("interpreting out of range value %d as 1 FIXME\n", val)); 4152 out->pv_multiplier = 1U; 4153 } else { 4154 out->pv_multiplier = (unsigned) val; 4155 } 4156 } 4157 break; 4158 case 'N': 4159 case 'n': 4160 TRACE(("write control negative pattern control \"%s\"\n", 4161 fragment_to_tempstr(arg))); 4162 { 4163 int val; 4164 if (!regis_num_to_int(arg, &val)) { 4165 val = -1; 4166 } 4167 switch (val) { 4168 default: 4169 TRACE(("interpreting out of range value %d as 0 FIXME\n", val)); 4170 out->invert_pattern = 0U; 4171 break; 4172 case 0: 4173 out->invert_pattern = 0U; 4174 break; 4175 case 1: 4176 out->invert_pattern = 1U; 4177 break; 4178 } 4179 } 4180 break; 4181 case 'P': 4182 case 'p': 4183 TRACE(("write control found pattern control \"%s\"\n", 4184 fragment_to_tempstr(arg))); 4185 { 4186 RegisDataFragment suboptionset; 4187 RegisDataFragment suboptionarg; 4188 RegisDataFragment item; 4189 char suboption; 4190 4191 while (!fragment_consumed(arg)) { 4192 if (skip_regis_whitespace(arg)) 4193 continue; 4194 4195 TRACE(("looking for option in \"%s\"\n", 4196 fragment_to_tempstr(arg))); 4197 if (extract_regis_parenthesized_data(arg, &suboptionset)) { 4198 TRACE(("got write pattern suboptionset: \"%s\"\n", 4199 fragment_to_tempstr(&suboptionset))); 4200 while (!fragment_consumed(&suboptionset)) { 4201 skip_regis_whitespace(&suboptionset); 4202 if (extract_regis_option(&suboptionset, &suboption, 4203 &suboptionarg)) { 4204 skip_regis_whitespace(&suboptionarg); 4205 TRACE(("inspecting write pattern suboption \"%c\" with value \"%s\"\n", 4206 suboption, 4207 fragment_to_tempstr(&suboptionarg))); 4208 switch (suboption) { 4209 case 'M': 4210 case 'm': 4211 TRACE(("found pattern multiplier \"%s\"\n", 4212 fragment_to_tempstr(&suboptionarg))); 4213 { 4214 RegisDataFragment num; 4215 int val; 4216 4217 if (extract_regis_num(&suboptionarg, 4218 &num)) { 4219 if (!regis_num_to_int(&num, &val) 4220 || val < 1) { 4221 TRACE(("interpreting out of range pattern multiplier \"%s\" as 2 FIXME\n", 4222 fragment_to_tempstr(&num))); 4223 out->pattern_multiplier = 2U; 4224 } else { 4225 out->pattern_multiplier = 4226 (unsigned) val; 4227 } 4228 skip_regis_whitespace(&suboptionarg); 4229 } 4230 4231 if (!fragment_consumed(&suboptionarg)) { 4232 TRACE(("DATA_ERROR: unknown content after pattern multiplier \"%s\"\n", 4233 fragment_to_tempstr(&suboptionarg))); 4234 return 0; 4235 } 4236 } 4237 break; 4238 default: 4239 TRACE(("DATA_ERROR: unknown ReGIS write pattern suboption '%c' arg \"%s\"\n", 4240 suboption, 4241 fragment_to_tempstr(&suboptionarg))); 4242 return 0; 4243 } 4244 continue; 4245 } 4246 4247 TRACE(("DATA_ERROR: skipping unknown token in pattern control suboptionset (expecting option): \"%s\"\n", 4248 fragment_to_tempstr(&suboptionset))); 4249 pop_fragment(&suboptionset); 4250 } 4251 continue; 4252 } 4253 4254 TRACE(("looking for int in \"%s\"\n", 4255 fragment_to_tempstr(arg))); 4256 if (extract_regis_num(arg, &item)) { 4257 if (peek_fragment(&item) == '0' || 4258 peek_fragment(&item) == '1') { 4259 unsigned pattern = 0U; 4260 unsigned bitcount; 4261 4262 TRACE(("converting pattern bits \"%s\"\n", 4263 fragment_to_tempstr(&item))); 4264 for (bitcount = 0;; bitcount++) { 4265 char ch = pop_fragment(&item); 4266 if (ch == '\0') 4267 break; 4268 switch (ch) { 4269 case '0': 4270 if (bitcount < MAX_PATTERN_BITS) { 4271 pattern <<= 1U; 4272 } 4273 break; 4274 case '1': 4275 if (bitcount < MAX_PATTERN_BITS) { 4276 pattern <<= 1U; 4277 pattern |= 1U; 4278 } 4279 break; 4280 default: 4281 TRACE(("DATA_ERROR: unknown ReGIS write pattern bit value \"%c\"\n", 4282 ch)); 4283 return 0; 4284 } 4285 } 4286 4287 if (bitcount > 0U) { 4288 unsigned extrabits; 4289 4290 for (extrabits = 0; 4291 bitcount + extrabits < MAX_PATTERN_BITS; 4292 extrabits++) { 4293 if (pattern & (1U << (bitcount - 1U))) { 4294 pattern <<= 1U; 4295 pattern |= 1U; 4296 } else { 4297 pattern <<= 1U; 4298 } 4299 } 4300 } 4301 4302 out->pattern = pattern; 4303 } else { 4304 int val; 4305 4306 TRACE(("converting pattern id \"%s\"\n", 4307 fragment_to_tempstr(&item))); 4308 if (!regis_num_to_int(&item, &val)) 4309 val = -1; 4310 switch (val) { /* FIXME: exponential allowed? */ 4311 case 0: 4312 out->pattern = 0x00; /* solid bg */ 4313 break; 4314 case 1: 4315 out->pattern = 0xff; /* solid fg */ 4316 break; 4317 case 2: 4318 out->pattern = 0xf0; /* dash */ 4319 break; 4320 case 3: 4321 out->pattern = 0xe4; /* dash dot */ 4322 break; 4323 case 4: 4324 out->pattern = 0xaa; /* dot */ 4325 break; 4326 case 5: 4327 out->pattern = 0xea; /* dash dot dot */ 4328 break; 4329 case 6: 4330 out->pattern = 0x88; /* sparse dot */ 4331 break; 4332 case 7: 4333 out->pattern = 0x84; /* asymmetric sparse dot */ 4334 break; 4335 case 8: 4336 out->pattern = 0xc8; /* sparse dash dot */ 4337 break; 4338 case 9: 4339 out->pattern = 0x86; /* sparse dot dash */ 4340 break; 4341 default: 4342 TRACE(("DATA_ERROR: unknown ReGIS standard write pattern \"%d\"\n", 4343 val)); 4344 return 0; 4345 } 4346 } 4347 4348 TRACE(("final pattern is %02x\n", out->pattern)); 4349 continue; 4350 } 4351 skip_regis_whitespace(arg); 4352 4353 TRACE(("DATA_ERROR: skipping unknown token in pattern suboption: \"%s\"\n", 4354 fragment_to_tempstr(arg))); 4355 pop_fragment(arg); 4356 } 4357 } 4358 break; 4359 case 'R': 4360 case 'r': 4361 TRACE(("write control switch to replacement writing mode \"%s\"\n", 4362 fragment_to_tempstr(arg))); 4363 out->write_style = WRITE_STYLE_REPLACE; 4364 break; 4365 case 'S': 4366 case 's': 4367 TRACE(("write control shading control \"%s\"\n", 4368 fragment_to_tempstr(arg))); 4369 { 4370 RegisDataFragment suboptionset; 4371 RegisDataFragment suboptionarg; 4372 RegisDataFragment item; 4373 char suboption; 4374 char shading_character = '\0'; 4375 unsigned reference_dim = WRITE_SHADING_REF_Y; 4376 /* FIXME: are relative offsets additive? */ 4377 int ref_x = cur_x, ref_y = cur_y; 4378 int shading_enabled = 0; 4379 4380 while (!fragment_consumed(arg)) { 4381 if (skip_regis_whitespace(arg)) 4382 continue; 4383 4384 if (extract_regis_string(arg, state->temp, state->templen)) { 4385 TRACE(("found fill char \"%s\"\n", state->temp)); 4386 /* FIXME: allow longer strings, ignore extra chars, or treat as error? */ 4387 if (strlen(state->temp) != 1) { 4388 TRACE(("DATA_ERROR: expected exactly one char in fill string FIXME\n")); 4389 return 0; 4390 } 4391 shading_character = state->temp[0]; 4392 shading_enabled = 1; 4393 TRACE(("shading character is: '%c' (%d)\n", 4394 shading_character, (int) shading_character)); 4395 continue; 4396 } 4397 4398 if (extract_regis_parenthesized_data(arg, &suboptionset)) { 4399 skip_regis_whitespace(&suboptionset); 4400 TRACE(("got shading control suboptionset: \"%s\"\n", 4401 fragment_to_tempstr(&suboptionset))); 4402 while (!fragment_consumed(&suboptionset)) { 4403 if (skip_regis_whitespace(&suboptionset)) 4404 continue; 4405 if (extract_regis_option(&suboptionset, &suboption, 4406 &suboptionarg)) { 4407 TRACE(("inspecting write shading suboption \"%c\" with value \"%s\"\n", 4408 suboption, 4409 fragment_to_tempstr(&suboptionarg))); 4410 switch (suboption) { 4411 case 'X': 4412 case 'x': 4413 TRACE(("found horizontal shading suboption \"%s\"\n", 4414 fragment_to_tempstr(&suboptionarg))); 4415 if (!fragment_consumed(&suboptionarg)) { 4416 TRACE(("DATA_ERROR: unexpected value to horizontal shading suboption FIXME\n")); 4417 return 0; 4418 } 4419 reference_dim = WRITE_SHADING_REF_X; 4420 shading_enabled = 1; 4421 break; 4422 default: 4423 TRACE(("DATA_ERROR: unknown ReGIS write pattern suboption '%c' arg \"%s\"\n", 4424 suboption, 4425 fragment_to_tempstr(&suboptionarg))); 4426 return 0; 4427 } 4428 continue; 4429 } 4430 4431 TRACE(("DATA_ERROR: skipping unknown token in shading control suboptionset (expecting option): \"%s\"\n", 4432 fragment_to_tempstr(&suboptionset))); 4433 pop_fragment(&suboptionset); 4434 } 4435 continue; 4436 } 4437 4438 if (extract_regis_extent(arg, &item)) { 4439 TRACE(("found extent in shading option curr=%d,%d ref=%d,%d\n", 4440 cur_x, cur_y, ref_x, ref_y)); 4441 if (!load_regis_coord_extent(context, 4442 fragment_to_tempstr(&item), 4443 ref_x, ref_y, 4444 &ref_x, &ref_y)) { 4445 TRACE(("DATA_ERROR: unable to parse extent in write shading option '%c': \"%s\"\n", 4446 option, fragment_to_tempstr(&item))); 4447 return 0; 4448 } 4449 TRACE(("shading reference = %d,%d (%s)\n", ref_x, ref_y, 4450 ((reference_dim == WRITE_SHADING_REF_X) 4451 ? "X" 4452 : "Y"))); 4453 continue; 4454 } 4455 4456 if (extract_regis_num(arg, &item)) { 4457 if (!regis_num_to_int(&item, &shading_enabled)) { 4458 TRACE(("DATA_ERROR: unable to parse int in write shading option '%c': \"%s\"\n", 4459 option, fragment_to_tempstr(&item))); 4460 return 0; 4461 } 4462 if (shading_enabled < 0 || shading_enabled > 1) { 4463 TRACE(("interpreting out of range value %d as 0 FIXME\n", 4464 shading_enabled)); 4465 shading_enabled = 0; 4466 } 4467 TRACE(("shading enabled = %d\n", shading_enabled)); 4468 continue; 4469 } 4470 4471 if (skip_regis_whitespace(arg)) { 4472 continue; 4473 } 4474 4475 TRACE(("DATA_ERROR: skipping unknown token in shade suboption: \"%s\"\n", 4476 fragment_to_tempstr(arg))); 4477 pop_fragment(arg); 4478 } 4479 4480 if (shading_enabled) { 4481 out->shading_enabled = 1U; 4482 out->shading_reference_dim = reference_dim; 4483 out->shading_reference = ((reference_dim == WRITE_SHADING_REF_X) 4484 ? ref_x 4485 : ref_y); 4486 out->shading_character = shading_character; 4487 TRACE(("final shading state: enabled, dim=%d ref=%d, char=%c\n", 4488 out->shading_reference_dim, out->shading_reference, 4489 out->shading_character)); 4490 } else { 4491 /* FIXME: confirm there is no effect if shading isn't enabled 4492 * in the same command 4493 */ 4494 out->shading_enabled = 0U; 4495 TRACE(("final shading state: shading disabled\n")); 4496 } 4497 } 4498 break; 4499 case 'V': 4500 case 'v': 4501 TRACE(("write control switch to overlay writing mode \"%s\"\n", 4502 fragment_to_tempstr(arg))); 4503 out->write_style = WRITE_STYLE_OVERLAY; 4504 break; 4505 default: 4506 TRACE(("DATA_ERROR: ignoring unknown ReGIS write option \"%c\" arg \"%s\"\n", 4507 option, fragment_to_tempstr(arg))); 4508 return 0; 4509 } 4510 4511 return 1; 4512} 4513 4514static int 4515load_regis_write_control_set(RegisParseState *state, 4516 RegisGraphicsContext const *context, 4517 int cur_x, int cur_y, 4518 RegisDataFragment *controls, 4519 RegisWriteControls *out) 4520{ 4521 RegisDataFragment optionset; 4522 RegisDataFragment arg; 4523 char option; 4524 4525 while (!fragment_consumed(controls)) { 4526 if (skip_regis_whitespace(controls)) 4527 continue; 4528 4529 if (extract_regis_parenthesized_data(controls, &optionset)) { 4530 TRACE(("got write control optionset: \"%s\"\n", 4531 fragment_to_tempstr(&optionset))); 4532 while (!fragment_consumed(&optionset)) { 4533 skip_regis_whitespace(&optionset); 4534 if (extract_regis_option(&optionset, &option, &arg)) { 4535 skip_regis_whitespace(&arg); 4536 TRACE(("got write control option and value: \"%c\" \"%s\"\n", 4537 option, fragment_to_tempstr(&arg))); 4538 if (!load_regis_write_control(state, context, 4539 cur_x, cur_y, 4540 option, &arg, out)) { 4541 return 0; 4542 } 4543 continue; 4544 } 4545 4546 TRACE(("DATA_ERROR: skipping unknown token in write control optionset (expecting option): \"%s\"\n", 4547 fragment_to_tempstr(&optionset))); 4548 pop_fragment(&optionset); 4549 } 4550 continue; 4551 } 4552 4553 TRACE(("DATA_ERROR: skipping unknown token in write controls (expecting optionset): \"%s\"\n", 4554 fragment_to_tempstr(controls))); 4555 pop_fragment(controls); 4556 } 4557 4558 return 1; 4559} 4560 4561static void 4562init_regis_write_controls(int graphics_termid, unsigned all_planes, 4563 RegisWriteControls *controls) 4564{ 4565 controls->pv_multiplier = 1U; 4566 controls->pattern = 0xff; /* solid */ 4567 controls->pattern_multiplier = 2U; 4568 controls->invert_pattern = 0U; 4569 controls->plane_mask = all_planes; 4570 controls->write_style = WRITE_STYLE_OVERLAY; 4571 switch (graphics_termid) { 4572 case 125: /* FIXME: verify */ 4573 case 240: /* FIXME: verify */ 4574 case 241: /* FIXME: verify */ 4575 case 330: 4576 controls->foreground = 3U; 4577 break; 4578 case 340: 4579 default: 4580 controls->foreground = 7U; 4581 break; 4582 case 382: 4583 controls->foreground = 1U; /* FIXME: verify */ 4584 break; 4585 } 4586 controls->shading_enabled = 0U; 4587 controls->shading_character = '\0'; 4588 controls->shading_reference = 0; /* no meaning if shading is disabled */ 4589 controls->shading_reference_dim = WRITE_SHADING_REF_NONE; 4590 controls->line_width = 1U; 4591 /* FIXME: add the rest */ 4592} 4593 4594static void 4595map_regis_graphics_pages(XtermWidget xw, RegisGraphicsContext *context) 4596{ 4597 const int charrow = 0; 4598 const int charcol = 0; 4599 unsigned old_display_id = ~0U; 4600 4601 if (context->destination_graphic) 4602 context->destination_graphic->hidden = 1; 4603 if (context->display_graphic) { 4604 context->display_graphic->hidden = 1; 4605 old_display_id = context->display_graphic->id; 4606 } 4607 4608 context->destination_graphic = 4609 get_new_or_matching_graphic(xw, 4610 charrow, charcol, 4611 context->width, 4612 context->height, 4613 context->destination_page); 4614 if (context->destination_graphic) { 4615 context->destination_graphic->hidden = 1; 4616 context->destination_graphic->valid = 1; 4617 } 4618 4619 context->display_graphic = 4620 get_new_or_matching_graphic(xw, 4621 charrow, charcol, 4622 context->width, 4623 context->height, 4624 context->display_page); 4625 if (context->display_graphic) { 4626 context->display_graphic->hidden = 0; 4627 if (old_display_id != context->display_graphic->id) { 4628 if (!context->display_graphic->valid) { 4629 draw_solid_rectangle(context->display_graphic, 0, 0, 4630 context->width, context->height, 4631 context->background); 4632 } 4633 context->display_graphic->dirty = 1; 4634 context->force_refresh = 1; 4635 /* FIXME: This isn't really enough. If there are holes in the new 4636 * graphic they should be cleared and set to the text from the same 4637 * page. But we don't have pages for text in xterm (the alt buffer 4638 * is similar though). 4639 */ 4640 } 4641 context->display_graphic->valid = 1; 4642 } 4643 4644 TRACE(("using graphics destination=[%d -> %u] display=[%d -> %u]\n", 4645 context->destination_page, 4646 (context->destination_graphic 4647 ? context->destination_graphic->id 4648 : 0U), 4649 context->display_page, 4650 (context->display_graphic 4651 ? context->display_graphic->id 4652 : 0U))); 4653} 4654 4655static void 4656copy_regis_write_controls(RegisWriteControls const *src, 4657 RegisWriteControls *dst) 4658{ 4659 dst->pv_multiplier = src->pv_multiplier; 4660 dst->pattern = src->pattern; 4661 dst->pattern_multiplier = src->pattern_multiplier; 4662 dst->invert_pattern = src->invert_pattern; 4663 dst->foreground = src->foreground; 4664 dst->plane_mask = src->plane_mask; 4665 dst->write_style = src->write_style; 4666 dst->shading_enabled = src->shading_enabled; 4667 dst->shading_character = src->shading_character; 4668 dst->shading_reference = src->shading_reference; 4669 dst->shading_reference_dim = src->shading_reference_dim; 4670 dst->line_width = src->line_width; 4671} 4672 4673static void 4674init_regis_text_controls(RegisTextControls *controls) 4675{ 4676 controls->alphabet_num = 0U; /* built-in */ 4677 controls->character_set_l = 0U; /* ASCII */ 4678 controls->character_set_r = 0U; /* Latin-1 */ 4679 get_standard_character_size(1, &controls->character_display_w, 4680 &controls->character_display_h, 4681 &controls->character_unit_cell_w, 4682 &controls->character_unit_cell_h, 4683 &controls->character_inc_x, 4684 &controls->character_inc_y); 4685 controls->string_rotation = 0; 4686 controls->character_rotation = 0; 4687 controls->slant = 0; 4688} 4689 4690static void 4691copy_regis_text_controls(RegisTextControls const *src, RegisTextControls *dst) 4692{ 4693 dst->alphabet_num = src->alphabet_num; 4694 dst->character_set_l = src->character_set_l; 4695 dst->character_set_r = src->character_set_r; 4696 dst->character_display_w = src->character_display_w; 4697 dst->character_display_h = src->character_display_h; 4698 dst->character_unit_cell_w = src->character_unit_cell_w; 4699 dst->character_unit_cell_h = src->character_unit_cell_h; 4700 dst->character_inc_x = src->character_inc_x; 4701 dst->character_inc_y = src->character_inc_y; 4702 dst->string_rotation = src->string_rotation; 4703 dst->character_rotation = src->character_rotation; 4704 dst->slant = src->slant; 4705} 4706 4707static void 4708init_regis_alphabets(RegisGraphicsContext *context) 4709{ 4710 unsigned alphabet_index; 4711 4712 for (alphabet_index = 0U; alphabet_index < MAX_REGIS_ALPHABETS; 4713 alphabet_index++) { 4714 context->alphabets[alphabet_index].alphabet_num = INVALID_ALPHABET_NUM; 4715 context->alphabets[alphabet_index].pixw = 0U; 4716 context->alphabets[alphabet_index].pixh = 0U; 4717 context->alphabets[alphabet_index].name[0] = '\0'; 4718 context->alphabets[alphabet_index].fontname[0] = '\0'; 4719 context->alphabets[alphabet_index].use_font = 0; 4720 context->alphabets[alphabet_index].bytes = NULL; 4721 } 4722} 4723 4724static void 4725init_regis_graphics_context(int graphics_termid, int width, int height, 4726 unsigned max_colors, const char *builtin_font, 4727 RegisGraphicsContext *context) 4728{ 4729 context->destination_graphic = NULL; 4730 context->display_graphic = NULL; 4731 context->display_page = 0U; 4732 context->destination_page = 0U; 4733 context->graphics_termid = graphics_termid; 4734 4735 /* reset addressing / clear user coordinates */ 4736 context->width = width; 4737 context->height = height; 4738 context->x_off = 0; 4739 context->y_off = 0; 4740 context->x_div = width - 1; 4741 context->y_div = height - 1; 4742 4743 /* 4744 * Generate a mask covering all valid color register address bits 4745 * (but don't bother past 2**16). 4746 */ 4747 context->all_planes = max_colors; 4748 context->all_planes--; 4749 context->all_planes |= 1U; 4750 context->all_planes |= context->all_planes >> 1U; 4751 context->all_planes |= context->all_planes >> 2U; 4752 context->all_planes |= context->all_planes >> 4U; 4753 context->all_planes |= context->all_planes >> 8U; 4754 4755 context->builtin_font = builtin_font; 4756 4757 init_regis_write_controls(graphics_termid, context->all_planes, 4758 &context->persistent_write_controls); 4759 copy_regis_write_controls(&context->persistent_write_controls, 4760 &context->temporary_write_controls); 4761 4762 init_regis_text_controls(&context->persistent_text_controls); 4763 context->current_text_controls = &context->persistent_text_controls; 4764 init_regis_alphabets(context); 4765 4766 context->multi_input_mode = 0; 4767 /* FIXME: coordinates */ 4768 /* FIXME: scrolling */ 4769 context->background = 0U; 4770 /* FIXME: input cursor location */ 4771 /* FIXME: input cursor style */ 4772 context->graphics_output_cursor_x = 0; 4773 context->graphics_output_cursor_y = 0; 4774 /* FIXME: output cursor style */ 4775 4776 context->force_refresh = 0; 4777} 4778 4779static int 4780parse_regis_command(RegisParseState *state) 4781{ 4782 char ch; 4783 4784 if (!extract_regis_command(&state->input, &ch)) 4785 return 0; 4786 4787 switch (ch) { 4788 case 'C': 4789 case 'c': 4790 /* Curve 4791 4792 * C 4793 * (A) # set the arc length in degrees (+ or nothing for 4794 * # counter-clockwise, - for clockwise, rounded to the 4795 * # closest integer degree) 4796 * (B) # begin closed curve sequence (must have at least two 4797 * # values; this option can not be nested) 4798 * (C) # position is the center, current location is the 4799 * # circumference (stays in effect until next command) 4800 * (E) # end curve sequence (drawing is performed here) 4801 * (S) # begin open curve sequence 4802 * (W) # temporary write options (see write command) 4803 * [<center, circumference position>] # center if (C), otherwise point on circumference 4804 * [<point in curve sequence>]... # if between (B) and (E) 4805 * <pv>... # if between (B) and (E) 4806 */ 4807 TRACE(("found ReGIS command \"%c\" (curve)\n", ch)); 4808 state->command = 'c'; 4809 state->curve_mode = CURVE_POSITION_ARC_EDGE; 4810 state->arclen = 360; 4811 state->num_points = 0U; 4812 break; 4813 case 'F': 4814 case 'f': 4815 /* Fill 4816 4817 * F 4818 * (V) # polygon (see vector command) 4819 * (C) # curve (see curve command) 4820 * (W) # temporary write options (see write command) 4821 */ 4822 TRACE(("found ReGIS command \"%c\" (filled polygon)\n", ch)); 4823 state->command = 'f'; 4824 break; 4825 case 'L': 4826 case 'l': 4827 /* Load 4828 4829 * L 4830 * (A) # set alphabet number or name 4831 * (F)"fontname" # load from font (xterm extension) 4832 * (S)[w,h] # set glyph size (xterm extension) 4833 * "ascii"xx,xx,xx,xx,xx,xx,xx,xx # pixel values 4834 */ 4835 TRACE(("found ReGIS command \"%c\" (load charset)\n", ch)); 4836 state->command = 'l'; 4837 break; 4838 case 'P': 4839 case 'p': 4840 /* Position 4841 4842 * P 4843 * (B) # begin bounded position stack (last point returns to first) 4844 * (E) # end position stack 4845 * (P) # select graphics page for the input and output cursors 4846 * (S) # begin unbounded position stack 4847 * (W) # temporary write options (see write command) 4848 * <pv> # move: 0 == right, 1 == upper right, ..., 7 == lower right 4849 * [<position>] # move to position (X, Y, or both) 4850 * 4851 * Note the stack does not need to be ended before the next command 4852 * Note: maximum depth is 16 levels 4853 */ 4854 TRACE(("found ReGIS command \"%c\" (position)\n", ch)); 4855 state->command = 'p'; 4856 break; 4857 case 'R': 4858 case 'r': 4859 /* Report 4860 4861 * R 4862 * (E) # parse error 4863 * (I<val>) # set input mode (0 == one-shot, 1 == multiple) (always returns CR) 4864 * (L) # current alphabet number and name 4865 * (M(<name>) # macrograph contents 4866 * (M(=) # macrograph storage (free bytes of total bytes) 4867 * (P) # absolute output cursor position 4868 * (P(I)) # interactive locator mode (in one-shot or multiple mode) 4869 * (P(I[xmul,ymul])) # interactive locator mode with arrow key movement multipliers 4870 */ 4871 TRACE(("found ReGIS command \"%c\" (report status)\n", ch)); 4872 state->command = 'r'; 4873 break; 4874 case 'S': 4875 case 's': 4876 /* Screen 4877 4878 * S 4879 * (A[<upper left>][<lower right>]) # adjust screen coordinates 4880 * (C<setting> # 0 (cursor output off), 1 (cursor output on) 4881 * (E) # erase to background color, resets shades, curves, and stacks 4882 * (F) # print the graphic and erase the screen (DECprint extension) 4883 * (H(P<printer offset>)[<print area cornet>][<print area corner>) 4884 * (I<color register>) # set the background to a specific register 4885 * (I(<rgbcode>)) # set the background to the register closest to an "RGB" color 4886 * (I(R<r>G<g>B<b>)) # set the background to the register closest to an RGB triplet (RLogin extension) 4887 * (I(H<h>L<l>S<s>)) # set the background to the register closest to an HLS triplet 4888 * (I(L<l>)) # set the background to the register closest to a grayscale value 4889 * (M<color index to set>(<rgbcode>)...) # codes are D (black), R (red), G (green), B (blue), C (cyan), Y (yellow), M (magenta), W (white) (sets color and grayscale registers) 4890 * (M<color index to set>(A<rgbcode>)...) # codes are D (black), R (red), G (green), B (blue), C (cyan), Y (yellow), M (magenta), W (white) (sets color registers only) 4891 * (M<color index to set>(R<red>G<green>B<blue>)...) # 0..100, 0..100, 0..100 (sets color and grayscale registers) (RLogin extension) 4892 * (M<color index to set>(AR<red>G<green>B<blue>)...) # 0..100, 0..100, 0..100 (sets color registers only) (RLogin extension) 4893 * (M<color index to set>(H<hue>L<lightness>S<saturation>)...) # 0..360, 0..100, 0..100 (sets color and grayscale registers) 4894 * (M<color index to set>(AH<hue>L<lightness>S<saturation>)...) # 0..360, 0..100, 0..100 (sets color registers only) 4895 * (M<color index to set>(L<mono level>)...) # level is 0 ... 100 (sets grayscale registers only) 4896 * (P<graphics page number>) # 0 (default) or 1 4897 * (S(<scale>) # scale screen output by scale (default 1, VT125:max=2, VT3x0:unsupported) FIXME 4898 * (S(X<scale>) # scale screen output horizontally by scale (default 1, VT125:max=2, VT3x0:unsupported) FIXME 4899 * (S(Y<scale>) # scale screen output vertically by scale (default 1, VT125:max=2, VT3x0:unsupported) FIXME 4900 * (T(<time delay ticks>) # delay (60 ticks is one second, up to 32767 ticks) 4901 * (N<setting>) # 0 == normal video, 1 == negative/reverse video (not supported on VT3x0) 4902 * (W(M<factor>) # PV multiplier 4903 * <PV scroll offset> # scroll data so given coordinate is at the upper-left 4904 * [scroll offset] # scroll data so given coordinate is at the upper-left 4905 */ 4906 TRACE(("found ReGIS command \"%c\" (screen)\n", ch)); 4907 state->command = 's'; 4908 break; 4909 case 'T': 4910 case 't': 4911 /* Text 4912 4913 * T 4914 * (A) # specify which alphabet/font to select glyphs from (0==builtin) 4915 * (A0L"<designator>")) # specify a built-in set for GL via two-char designator 4916 * (A0R"<designator>")) # specify a built-in set for GR via two-char or three-char designator 4917 * (A<num>R"<designator>")) # specify a user-loaded (1-3) set for GR via two-char or three-char designator 4918 * (B) # begin temporary text control 4919 * (D<char angle>) # specify a character tilt 4920 * (D<str angle>S<size id>) # specify a string tilt 4921 * (D<str angle>S<size id>D<char angle>) # specify a string and character tilt 4922 * (E) # end temporary text control 4923 * (H<factor>) # select a height multiplier (GIGI:1-16, VT340:1-256) 4924 * (I<angle>) # italic/oblique: no slant (0), lean forward (-1 though -45), lean back (+1 through +45) 4925 * (M[width factor,height factor]) # select size multipliers (width 1-16) (height 1-256) 4926 * (S<size id>) # select one of the 17 standard character sizes 4927 * (S[dimensions]) # set a custom display cell size (char with border) 4928 * (U[dimensions]) # set a custom unit cell size (char size) 4929 * (W<write command>) # temporary write options (see write command) 4930 * [<char offset>] # optional manual offset between characters 4931 * <PV spacing> # move half-increments for subscripts and superscripts 4932 * '<text>' # optional 4933 * "<text>" # optional 4934 */ 4935 TRACE(("found ReGIS command \"%c\" (text)\n", ch)); 4936 state->command = 't'; 4937 state->text_tilt_state = TEXT_TILT_STATE_READY; 4938 break; 4939 case 'V': 4940 case 'v': 4941 /* Vector 4942 4943 * V 4944 * (B) # begin bounded position stack (last point returns to first) 4945 * (E) # end position stack 4946 * (S) # begin unbounded position stack 4947 * (W) # temporary write options (see write command) 4948 * <pv> # draw a line to the pixel vector 4949 * [] # draw a dot at the current location 4950 * [<position>] # draw a line to position 4951 */ 4952 TRACE(("found ReGIS command \"%c\" (vector)\n", ch)); 4953 state->command = 'v'; 4954 break; 4955 case 'W': 4956 case 'w': 4957 /* Write 4958 4959 * W 4960 * (A<setting>) # 0 == disable alternate, 1 == enable alternate/blink FIXME 4961 * (C) # complement writing mode 4962 * (E) # erase writing mode 4963 * (F<plane>) # set the plane mask to control which pixel bits are updated 4964 * (I<color register>) # set the foreground to a specific register 4965 * (I(<rgbcode>)) # set the foreground to the register closest to an "RGB" color 4966 * (I(R<r>G<g>B<b>)) # set the foreground to the register closest to an RGB triplet (RLogin extension) 4967 * (I(H<h>L<l>S<s>)) # set the foreground to the register closest to an HLS triplet 4968 * (I(L<l>)) # set the foreground to the register closest to a grayscale value 4969 * (L<width>) # set the line width (RLogin extension) FIXME 4970 * (M<pixel vector multiplier>) # set the multiplication factor 4971 * (N<setting>) # 0 == negative patterns disabled, 1 == negative patterns enabled 4972 * (P<pattern number>) # 0..9: 0 == none, 1 == solid, 2 == 50% dash, 3 == dash-dot 4973 * (P<pattern bits>) # 2 to 8 bits represented as a 0/1 sequence 4974 * (P<(M<pattern multiplier>)) # set the pattern multiplier 4975 * (R) # replacement writing mode 4976 * (S'<character>') # set shading character 4977 * (S<setting>) # 0 == disable shading, 1 == enable shading 4978 * (S[reference point]) # set a horizontal reference line including this point (X ignored) 4979 * (S(X)[reference point]) # set a vertical reference line including this point 4980 * (V) # overlay writing mode 4981 */ 4982 TRACE(("found ReGIS command \"%c\" (write parameters)\n", ch)); 4983 state->command = 'w'; 4984 break; 4985 case '@': 4986 /* Macrograph 4987 4988 * . # clear all macrographs 4989 * :<letter> ...@; # define macrograph for letter 4990 * <letter> # expand macrograph for letter 4991 */ 4992 TRACE(("found ReGIS macrograph command\n")); 4993 state->command = '@'; 4994 break; 4995 default: 4996 TRACE(("DATA_ERROR: unknown ReGIS command %04x (%c), setting to '_'\n", 4997 (int) ch, ch)); 4998 state->command = '_'; 4999 state->option = '_'; 5000 return 0; 5001 } 5002 5003 state->option = '_'; 5004 5005 return 1; 5006} 5007 5008static int 5009parse_regis_option(RegisParseState *state, RegisGraphicsContext *context) 5010{ 5011 RegisDataFragment optionarg; 5012 5013 if (!extract_regis_option(&state->input, &state->option, &optionarg)) 5014 return 0; 5015 skip_regis_whitespace(&optionarg); 5016 5017 TRACE(("found ReGIS option '%c', parsing argument \"%s\"\n", 5018 state->option, fragment_to_tempstr(&optionarg))); 5019 5020 switch (state->command) { 5021 case 'c': 5022 TRACE(("inspecting curve option \"%c\" with value \"%s\"\n", 5023 state->option, fragment_to_tempstr(&optionarg))); 5024 switch (state->option) { 5025 case 'A': 5026 case 'a': 5027 TRACE(("found arc length \"%s\"\n", 5028 fragment_to_tempstr(&optionarg))); 5029 { 5030 RegisDataFragment arclen; 5031 5032 if (!extract_regis_num(&optionarg, &arclen)) { 5033 TRACE(("DATA_ERROR: expected int in curve arclen option: \"%s\"\n", 5034 fragment_to_tempstr(&optionarg))); 5035 break; 5036 } 5037 TRACE(("arc length string %s\n", 5038 fragment_to_tempstr(&arclen))); 5039 if (!regis_num_to_int(&arclen, &state->arclen)) { 5040 TRACE(("DATA_ERROR: unable to parse int in curve arclen option: \"%s\"\n", 5041 fragment_to_tempstr(&arclen))); 5042 break; 5043 } 5044 TRACE(("value of arc length is %d\n", state->arclen)); 5045 while (state->arclen < -360) 5046 state->arclen += 360; 5047 while (state->arclen > 360) 5048 state->arclen -= 360; 5049 TRACE(("using final arc length %d\n", state->arclen)); 5050 5051 skip_regis_whitespace(&optionarg); 5052 if (!fragment_consumed(&optionarg)) { 5053 TRACE(("DATA_ERROR: ignoring trailing junk in arc length option \"%s\"\n", 5054 fragment_to_tempstr(&optionarg))); 5055 break; 5056 } 5057 } 5058 break; 5059 case 'B': 5060 case 'b': 5061 TRACE(("begin closed curve \"%s\"\n", 5062 fragment_to_tempstr(&optionarg))); 5063 if (!fragment_consumed(&optionarg)) { 5064 TRACE(("DATA_ERROR: invalid closed curve option \"%s\"\n", 5065 fragment_to_tempstr(&optionarg))); 5066 break; 5067 } 5068 state->curve_mode = CURVE_POSITION_CLOSED_CURVE; 5069 state->num_points = 0U; 5070 state->x_points[state->num_points] = 5071 context->graphics_output_cursor_x; 5072 state->y_points[state->num_points] = 5073 context->graphics_output_cursor_y; 5074 state->num_points++; 5075 break; 5076 case 'C': 5077 case 'c': 5078 TRACE(("found center position mode \"%s\"\n", 5079 fragment_to_tempstr(&optionarg))); 5080 if (!fragment_consumed(&optionarg)) { 5081 TRACE(("DATA_ERROR: invalid center position option \"%s\"\n", 5082 fragment_to_tempstr(&optionarg))); 5083 break; 5084 } 5085 state->curve_mode = CURVE_POSITION_ARC_CENTER; 5086 break; 5087 case 'E': 5088 case 'e': 5089 TRACE(("found end curve \"%s\"\n", fragment_to_tempstr(&optionarg))); 5090 if (!fragment_consumed(&optionarg)) { 5091 TRACE(("DATA_ERROR: ignoring unexpected arguments to curve option '%c' arg \"%s\"\n", 5092 state->option, fragment_to_tempstr(&optionarg))); 5093 } 5094 5095 switch (state->curve_mode) { 5096 case CURVE_POSITION_CLOSED_CURVE: 5097 { 5098 unsigned i; 5099 5100#ifdef DEBUG_SPLINE_POINTS 5101 printf("points: \n"); 5102 for (i = 0; i < state->num_points; i++) 5103 printf(" %d,%d\n", 5104 state->x_points[i], state->y_points[i]); 5105#endif 5106 5107#ifdef DEBUG_SPLINE_WITH_ROTATION 5108 { 5109 static unsigned shift = 0; 5110 int temp_x[MAX_CURVE_POINTS], temp_y[MAX_CURVE_POINTS]; 5111 shift++; 5112 shift = shift % state->num_points; 5113 for (i = 0; i < state->num_points; i++) { 5114 temp_x[i] = state->x_points[i]; 5115 temp_y[i] = state->y_points[i]; 5116 } 5117 for (i = 0; i < state->num_points; i++) { 5118 state->x_points[i] = 5119 temp_x[(i + shift) % state->num_points]; 5120 state->y_points[i] = 5121 temp_y[(i + shift) % state->num_points]; 5122 } 5123 5124#ifdef DEBUG_SPLINE_POINTS 5125 printf("after shift %d: \n", shift); 5126 for (i = 0; i < state->num_points; i++) 5127 printf(" %d,%d\n", 5128 state->x_points[i], state->y_points[i]); 5129#endif 5130 } 5131#endif 5132 5133 for (i = state->num_points; i > 0; i--) { 5134 state->x_points[i] = state->x_points[i - 1]; 5135 state->y_points[i] = state->y_points[i - 1]; 5136 } 5137 state->x_points[0] = state->x_points[state->num_points]; 5138 state->y_points[0] = state->y_points[state->num_points]; 5139 state->num_points++; 5140 for (i = state->num_points; i != 0; i--) { 5141 state->x_points[i] = state->x_points[i - 1]; 5142 state->y_points[i] = state->y_points[i - 1]; 5143 } 5144 state->x_points[0] = state->x_points[state->num_points - 1]; 5145 state->y_points[0] = state->y_points[state->num_points - 1]; 5146 state->num_points++; 5147 state->x_points[state->num_points] = state->x_points[2]; 5148 state->y_points[state->num_points] = state->y_points[2]; 5149 state->num_points++; 5150#ifdef DEBUG_SPLINE_WITH_OVERDRAW 5151 state->x_points[state->num_points] = state->x_points[3]; 5152 state->y_points[state->num_points] = state->y_points[3]; 5153 state->num_points++; 5154 state->x_points[state->num_points] = state->x_points[4]; 5155 state->y_points[state->num_points] = state->y_points[4]; 5156 state->num_points++; 5157#endif 5158#ifdef DEBUG_SPLINE_POINTS 5159 printf("after points added: \n"); 5160 for (i = 0; i < state->num_points; i++) 5161 printf(" %d,%d\n", 5162 state->x_points[i], state->y_points[i]); 5163#endif 5164 } 5165 5166 TRACE(("drawing closed spline\n")); 5167 TRACE(("output location was: %d,%d\n", 5168 context->graphics_output_cursor_x, 5169 context->graphics_output_cursor_y)); 5170 global_context = context; /* FIXME: remove after updating spline code */ 5171 plotCubicSpline((int) state->num_points - 1, 5172 state->x_points, state->y_points, 5173 1); 5174 TRACE(("output location now: %d,%d\n", 5175 context->graphics_output_cursor_x, 5176 context->graphics_output_cursor_y)); 5177 TRACE(("output location finally: %d,%d\n", 5178 context->graphics_output_cursor_x, 5179 context->graphics_output_cursor_y)); 5180 state->num_points = 0U; 5181 break; 5182 case CURVE_POSITION_OPEN_CURVE: 5183#ifdef DEBUG_SPLINE_POINTS 5184 { 5185 unsigned i; 5186 5187 printf("points: \n"); 5188 for (i = 0U; i < state->num_points; i++) 5189 printf(" %d,%d\n", 5190 state->x_points[i], state->y_points[i]); 5191 } 5192#endif 5193 TRACE(("drawing open spline\n")); 5194 TRACE(("output location was: %d,%d\n", 5195 context->graphics_output_cursor_x, 5196 context->graphics_output_cursor_y)); 5197 global_context = context; /* FIXME: remove after updating spline code */ 5198 plotCubicSpline((int) state->num_points - 1, 5199 state->x_points, state->y_points, 5200 1); 5201 TRACE(("output location now: %d,%d\n", 5202 context->graphics_output_cursor_x, 5203 context->graphics_output_cursor_y)); 5204 5205 context->graphics_output_cursor_x = 5206 state->x_points[state->num_points - 1]; 5207 context->graphics_output_cursor_y = 5208 state->y_points[state->num_points - 1]; 5209 TRACE(("output location finally: %d,%d\n", 5210 context->graphics_output_cursor_x, 5211 context->graphics_output_cursor_y)); 5212 state->num_points = 0U; 5213 break; 5214 default: 5215 TRACE(("DATA_ERROR: end curve option unexpected \"%s\"\n", 5216 fragment_to_tempstr(&optionarg))); 5217 break; 5218 } 5219 break; 5220 case 'S': 5221 case 's': 5222 TRACE(("begin open curve \"%s\"\n", 5223 fragment_to_tempstr(&optionarg))); 5224 if (!fragment_consumed(&optionarg)) { 5225 TRACE(("DATA_ERROR: invalid open curve option \"%s\"\n", 5226 fragment_to_tempstr(&optionarg))); 5227 break; 5228 } 5229 state->curve_mode = CURVE_POSITION_OPEN_CURVE; 5230 state->num_points = 0U; 5231 state->x_points[state->num_points] = 5232 context->graphics_output_cursor_x; 5233 state->y_points[state->num_points] = 5234 context->graphics_output_cursor_y; 5235 state->num_points++; 5236 TRACE(("first point on curve with location %d,%d\n", 5237 context->graphics_output_cursor_x, 5238 context->graphics_output_cursor_y)); 5239 break; 5240 case 'W': 5241 case 'w': 5242 TRACE(("found temporary write options \"%s\"\n", 5243 fragment_to_tempstr(&optionarg))); 5244 if (!load_regis_write_control_set(state, context, 5245 context->graphics_output_cursor_x, 5246 context->graphics_output_cursor_y, 5247 &optionarg, 5248 &context->temporary_write_controls)) { 5249 TRACE(("DATA_ERROR: invalid temporary write options \"%s\"\n", 5250 fragment_to_tempstr(&optionarg))); 5251 break; 5252 } 5253 break; 5254 default: 5255 TRACE(("DATA_ERROR: ignoring unknown ReGIS curve command option '%c' arg \"%s\"\n", 5256 state->option, fragment_to_tempstr(&optionarg))); 5257 break; 5258 } 5259 break; 5260 case 'f': 5261 TRACE(("ERROR: fill commands should not be handled here\n")); 5262 break; 5263 case 'l': 5264 TRACE(("inspecting load option \"%c\" with value \"%s\"\n", 5265 state->option, fragment_to_tempstr(&optionarg))); 5266 switch (state->option) { 5267 case 'A': 5268 case 'a': 5269 TRACE(("found alphabet specifier option \"%s\"\n", 5270 fragment_to_tempstr(&optionarg))); 5271 for (;;) { 5272 RegisDataFragment alphabetarg; 5273 5274 if (extract_regis_num(&optionarg, &alphabetarg)) { 5275 int alphabet; 5276 5277 TRACE(("alphabet number: %s\n", 5278 fragment_to_tempstr(&alphabetarg))); 5279 if (!regis_num_to_int(&alphabetarg, &alphabet)) { 5280 TRACE(("DATA_ERROR: unable to parse int in load alphabet option: \"%s\"\n", 5281 fragment_to_tempstr(&alphabetarg))); 5282 break; 5283 } 5284 if (alphabet < 0 || 5285 (unsigned) alphabet >= MAX_REGIS_ALPHABETS) { 5286 TRACE(("DATA_ERROR: invalid alphabet: \"%d\"\n", 5287 alphabet)); 5288 break; 5289 } 5290#ifndef ENABLE_UPLOAD_ALPHABET_ZERO 5291 if (alphabet == 0) { 5292 TRACE(("DATA_ERROR: alphabet 0 can not be modified\n")); 5293 break; 5294 } 5295#endif 5296 5297 TRACE(("setting load alphabet: %d\n", alphabet)); 5298 init_regis_load_state(state); 5299 state->load_alphabet = (unsigned) alphabet; 5300 } else if (extract_regis_string(&optionarg, state->temp, 5301 state->templen)) { 5302 TRACE(("alphabet name: %s\n", state->temp)); 5303 if (strlen(state->temp) == 0U || 5304 strlen(state->temp) >= REGIS_ALPHABET_NAME_LEN) { 5305 TRACE(("DATA_ERROR: alphabet names must be between 1 and %u characters long: \"%s\" FIXME\n", 5306 REGIS_ALPHABET_NAME_LEN - 1U, state->temp)); 5307 break; 5308 } 5309 5310 strcpy(state->load_name, state->temp); 5311 TRACE(("using name for alphabet %u: %s\n", 5312 state->load_alphabet, state->load_name)); 5313 } else if (skip_regis_whitespace(&optionarg)) { 5314 ; 5315 } else if (fragment_consumed(&optionarg)) { 5316 break; 5317 } else { 5318 TRACE(("DATA_ERROR: expected int or string in load alphabet option: \"%s\"\n", 5319 fragment_to_tempstr(&optionarg))); 5320 break; 5321 } 5322 } 5323 break; 5324#ifdef ENABLE_UPLOAD_ALPHABET_FROM_FONT 5325 case 'F': 5326 case 'f': 5327 TRACE(("found font option \"%s\"\n", 5328 fragment_to_tempstr(&optionarg))); 5329 5330 if (state->load_index == MAX_REGIS_ALPHABETS) { 5331 state->load_index = find_free_alphabet_index(context, 5332 state->load_alphabet, 5333 state->load_w, 5334 state->load_h); 5335 TRACE(("current alphabet is %u and size is %ux%u; assigning alphabet index %u\n", 5336 state->load_alphabet, state->load_w, 5337 state->load_h, state->load_index)); 5338 } 5339 5340 for (;;) { 5341 RegisDataFragment fontarg; 5342 5343 if (skip_regis_whitespace(&optionarg)) 5344 continue; 5345 if (extract_regis_num(&optionarg, &fontarg)) { 5346 int enabled; 5347 5348 TRACE(("fontname enabled: %s\n", 5349 fragment_to_tempstr(&fontarg))); 5350 if (!regis_num_to_int(&fontarg, &enabled)) { 5351 TRACE(("DATA_ERROR: unable to parse int in load fontname option: \"%s\"\n", 5352 fragment_to_tempstr(&fontarg))); 5353 break; 5354 } 5355 if (enabled != 0U && enabled != 1U) { 5356 TRACE(("DATA_ERROR: invalid fontname enable state: \"%d\"\n", enabled)); 5357 break; 5358 } 5359 5360 TRACE(("fontname enabled: %d\n", enabled)); 5361 context->alphabets[state->load_index].use_font = enabled; 5362 continue; 5363 } 5364 if (extract_regis_string(&optionarg, state->temp, 5365 state->templen)) { 5366 if (strlen(state->temp) == 0U || 5367 strlen(state->temp) >= REGIS_FONTNAME_LEN) { 5368 TRACE(("DATA_ERROR: font names must be between 1 and %u characters long: \"%s\"\n", 5369 REGIS_FONTNAME_LEN - 1U, state->temp)); 5370 break; 5371 } 5372 5373 strcpy(context->alphabets[state->load_index].fontname, 5374 state->temp); 5375 context->alphabets[state->load_index].use_font = 1; 5376 TRACE(("using backing font: %s\n", 5377 context->alphabets[state->load_index].fontname)); 5378 } 5379 5380 if (fragment_consumed(&optionarg)) { 5381 break; 5382 } else { 5383 TRACE(("DATA_ERROR: unexpected text in load fontname option: \"%s\"\n", 5384 fragment_to_tempstr(&optionarg))); 5385 break; 5386 } 5387 } 5388 break; 5389#endif 5390#ifdef ENABLE_USER_FONT_SIZE 5391 case 'S': 5392 case 's': 5393 TRACE(("found glyph size option \"%s\"\n", 5394 fragment_to_tempstr(&optionarg))); 5395 while (!fragment_consumed(&optionarg)) { 5396 RegisDataFragment sizearg; 5397 5398 if (skip_regis_whitespace(&optionarg)) 5399 continue; 5400 5401 if (extract_regis_extent(&optionarg, &sizearg)) { 5402 int w, h; 5403 unsigned size; 5404 5405 TRACE(("glyph size: %s\n", fragment_to_tempstr(&sizearg))); 5406 /* FIXME: verify that relative values don't work */ 5407 if (!load_regis_mult_extent(fragment_to_tempstr(&sizearg), 5408 &w, &h)) { 5409 TRACE(("DATA_ERROR: unable to parse extent in glyph size option: \"%s\"\n", 5410 fragment_to_tempstr(&sizearg))); 5411 break; 5412 } 5413 if (w < 1 || h < 1) { 5414 TRACE(("DATA_ERROR: glyph dimensions must not be negative or zero: %dx%d\n", 5415 w, h)); 5416 break; 5417 } 5418 size = GLYPH_WIDTH_BYTES((unsigned) w) * (unsigned) h; 5419 if (size > MAX_REGIS_ALPHABET_BYTES) { 5420 TRACE(("DATA_ERROR: glyph is too large (%u bytes, limit is %u bytes)\n", 5421 size, MAX_REGIS_ALPHABET_BYTES)); 5422 break; 5423 } 5424 5425 if (state->load_index != MAX_REGIS_ALPHABETS) { 5426 TRACE(("DATA_ERROR: glyph size can not be changed after any data is loaded\n")); 5427 break; 5428 } 5429 5430 TRACE(("using glyph size: %dx%d\n", w, h)); 5431 state->load_w = (unsigned) w; 5432 state->load_h = (unsigned) h; 5433 continue; 5434 } 5435 5436 TRACE(("DATA_ERROR: expected extent in glyph size option: \"%s\"\n", 5437 fragment_to_tempstr(&sizearg))); 5438 break; 5439 } 5440 break; 5441#endif 5442 default: 5443 TRACE(("DATA_ERROR: ignoring unknown ReGIS load command option '%c' arg \"%s\"\n", 5444 state->option, fragment_to_tempstr(&optionarg))); 5445 break; 5446 } 5447 break; 5448 case 'p': 5449 TRACE(("inspecting position option \"%c\" with value \"%s\"\n", 5450 state->option, fragment_to_tempstr(&optionarg))); 5451 switch (state->option) { 5452 case 'B': 5453 case 'b': 5454 TRACE(("found begin bounded position stack \"%s\"\n", 5455 fragment_to_tempstr(&optionarg))); 5456 skip_regis_whitespace(&optionarg); 5457 if (!fragment_consumed(&optionarg)) { 5458 TRACE(("DATA_ERROR: ignoring unexpected arguments to position option '%c' arg \"%s\"\n", 5459 state->option, fragment_to_tempstr(&optionarg))); 5460 } 5461 if (state->stack_next >= POSITION_STACK_SIZE) { 5462 /* FIXME: ignore, error, update counter? */ 5463 TRACE(("unable to push position to full stack\n")); 5464 break; 5465 } 5466 TRACE(("pushing location: %d,%d\n", 5467 context->graphics_output_cursor_x, 5468 context->graphics_output_cursor_y)); 5469 5470 state->stack_x[state->stack_next] = 5471 context->graphics_output_cursor_x; 5472 state->stack_y[state->stack_next] = 5473 context->graphics_output_cursor_y; 5474 state->stack_next++; 5475 break; 5476 case 'E': 5477 case 'e': 5478 TRACE(("found end position stack \"%s\"\n", 5479 fragment_to_tempstr(&optionarg))); 5480 skip_regis_whitespace(&optionarg); 5481 if (!fragment_consumed(&optionarg)) { 5482 TRACE(("DATA_ERROR: ignoring unexpected arguments to position option '%c' arg \"%s\"\n", 5483 state->option, fragment_to_tempstr(&optionarg))); 5484 } 5485 if (state->stack_next == 0U) { 5486 TRACE(("DATA_ERROR: unable to pop position from empty stack\n")); 5487 break; 5488 } 5489 5490 state->stack_next--; 5491 if (state->stack_x[state->stack_next] != DUMMY_STACK_X || 5492 state->stack_y[state->stack_next] != DUMMY_STACK_Y) { 5493 context->graphics_output_cursor_x = 5494 state->stack_x[state->stack_next]; 5495 context->graphics_output_cursor_y = 5496 state->stack_y[state->stack_next]; 5497 TRACE(("popped location: %d,%d\n", 5498 context->graphics_output_cursor_x, 5499 context->graphics_output_cursor_y)); 5500 } else { 5501 TRACE(("not popping location\n")); 5502 } 5503 break; 5504 case 'P': 5505 case 'p': 5506 TRACE(("found graphics page destination option \"%s\"\n", 5507 fragment_to_tempstr(&optionarg))); 5508 { 5509 RegisDataFragment pagearg; 5510 int page; 5511 5512 if (!extract_regis_num(&optionarg, &pagearg)) { 5513 TRACE(("DATA_ERROR: expected int in page destination option: \"%s\"\n", 5514 fragment_to_tempstr(&optionarg))); 5515 break; 5516 } 5517 TRACE(("page option arg: %s\n", fragment_to_tempstr(&pagearg))); 5518 if (!regis_num_to_int(&pagearg, &page)) { 5519 TRACE(("DATA_ERROR: unable to parse int in page destination option: \"%s\"\n", 5520 fragment_to_tempstr(&pagearg))); 5521 break; 5522 } 5523 if (page < 0 || (unsigned) page >= MAX_REGIS_PAGES) { 5524 TRACE(("DATA_ERROR: invalid page: \"%d\"\n", page)); 5525 break; 5526 } 5527 5528 TRACE(("using destination page number: %d\n", page)); 5529 context->destination_page = (unsigned) page; 5530 map_regis_graphics_pages(context->destination_graphic->xw, 5531 context); 5532 } 5533 break; 5534 case 'S': 5535 case 's': 5536 TRACE(("found begin unbounded position stack \"%s\"\n", 5537 fragment_to_tempstr(&optionarg))); 5538 skip_regis_whitespace(&optionarg); 5539 if (!fragment_consumed(&optionarg)) { 5540 TRACE(("DATA_ERROR: ignoring unexpected arguments to end position option '%c' arg \"%s\"\n", 5541 state->option, fragment_to_tempstr(&optionarg))); 5542 } 5543 if (state->stack_next >= POSITION_STACK_SIZE) { 5544 /* FIXME: ignore, error, update counter? */ 5545 TRACE(("unable to push dummy position to full stack\n")); 5546 break; 5547 } 5548 5549 TRACE(("pushing dummy positions instead of %d,%d\n", 5550 context->graphics_output_cursor_x, 5551 context->graphics_output_cursor_y)); 5552 state->stack_x[state->stack_next] = DUMMY_STACK_X; 5553 state->stack_y[state->stack_next] = DUMMY_STACK_Y; 5554 state->stack_next++; 5555 break; 5556 case 'W': 5557 case 'w': 5558 TRACE(("found temporary write options \"%s\"\n", 5559 fragment_to_tempstr(&optionarg))); 5560 if (!load_regis_write_control_set(state, context, 5561 context->graphics_output_cursor_x, 5562 context->graphics_output_cursor_y, 5563 &optionarg, 5564 &context->temporary_write_controls)) { 5565 TRACE(("DATA_ERROR: invalid temporary write options \"%s\"\n", 5566 fragment_to_tempstr(&optionarg))); 5567 } 5568 break; 5569 default: 5570 TRACE(("DATA_ERROR: ignoring unknown ReGIS position command option '%c' arg \"%s\"\n", 5571 state->option, fragment_to_tempstr(&optionarg))); 5572 break; 5573 } 5574 break; 5575 case 'r': 5576 TRACE(("inspecting report option \"%c\" with value \"%s\"\n", 5577 state->option, fragment_to_tempstr(&optionarg))); 5578 switch (state->option) { 5579 case 'E': 5580 case 'e': 5581 TRACE(("found parse error report \"%s\" FIXME\n", 5582 fragment_to_tempstr(&optionarg))); 5583 skip_regis_whitespace(&optionarg); 5584 if (!fragment_consumed(&optionarg)) { 5585 TRACE(("DATA_ERROR: unexpected arguments to ReGIS report command option '%c' arg \"%s\"\n", 5586 state->option, fragment_to_tempstr(&optionarg))); 5587 break; 5588 } { 5589 char reply[64]; 5590 5591 TRACE(("got report last error condition\n")); 5592 /* FIXME: implement after adding error tracking */ 5593 sprintf(reply, "\"%u,%u\"\r", 0U, 0U); 5594 unparseputs(context->display_graphic->xw, reply); 5595 unparse_end(context->display_graphic->xw); 5596 } 5597 break; 5598 case 'I': 5599 case 'i': 5600 TRACE(("found set input mode \"%s\"\n", 5601 fragment_to_tempstr(&optionarg))); 5602 { 5603 RegisDataFragment modearg; 5604 int mode; 5605 5606 if (!extract_regis_num(&optionarg, &modearg)) { 5607 TRACE(("DATA_ERROR: expected int in report input mode option: \"%s\"\n", 5608 fragment_to_tempstr(&modearg))); 5609 break; 5610 } 5611 5612 TRACE(("input mode: %s\n", fragment_to_tempstr(&modearg))); 5613 if (!regis_num_to_int(&modearg, &mode)) { 5614 TRACE(("DATA_ERROR: unable to parse int in report input mode option: \"%s\"\n", 5615 fragment_to_tempstr(&modearg))); 5616 break; 5617 } 5618 if (mode != 0 && mode != 1) { 5619 TRACE(("DATA_ERROR: ignoring invalid input mode: \"%d\"\n", 5620 mode)); 5621 break; 5622 } 5623 5624 TRACE(("using input mode: %d (%s)\n", mode, 5625 (mode == 0) 5626 ? "one-shot" 5627 : "multiple")); 5628 context->multi_input_mode = mode; 5629 if (context->multi_input_mode) { 5630 TRACE(("ERROR: multi-mode input not implemented FIXME\n")); 5631 /* FIXME: enable input cursor, send location on mouse clicks or non-arrowkey keypresses */ 5632 } else { 5633 /* FIXME: if in multi-mode, disable input cursor, stop tracking mouse clicks and keypresses */ 5634 /* FIXME: enable input cursor and disable drawing until location report request command is received */ 5635 /* FIXME: upon mouse click or keypress respond with location report */ 5636 } 5637 /* FIXME: implement input cursor */ 5638 /* FIXME: implement mouse tracking */ 5639 /* FIXME: implement arrow key movement */ 5640 /* FIXME: implement button/key collection */ 5641 5642 unparseputs(context->display_graphic->xw, "\r"); 5643 unparse_end(context->display_graphic->xw); 5644 5645 skip_regis_whitespace(&optionarg); 5646 if (!fragment_consumed(&optionarg)) { 5647 TRACE(("DATA_ERROR: unexpected arguments to ReGIS report command option '%c' arg \"%s\"\n", 5648 state->option, fragment_to_tempstr(&optionarg))); 5649 } 5650 /* FIXME: buffer commands until report request received */ 5651 } 5652 break; 5653 case 'L': 5654 case 'l': 5655 TRACE(("found character set load report \"%s\"\n", 5656 fragment_to_tempstr(&optionarg))); 5657 if (!fragment_consumed(&optionarg)) { 5658 TRACE(("DATA_ERROR: unexpected arguments to ReGIS report command option '%c' arg \"%s\"\n", 5659 state->option, fragment_to_tempstr(&optionarg))); 5660 break; 5661 } { 5662 char buffer[32]; 5663 5664 if (state->load_index == MAX_REGIS_ALPHABETS) { 5665 /* If this happens something went wrong elsewhere. */ 5666 TRACE(("DATA_ERROR: unable to report current load alphabet\n")); 5667 unparseputs(context->display_graphic->xw, "A0\"\"\r"); 5668 unparse_end(context->display_graphic->xw); 5669 break; 5670 } 5671 5672 unparseputs(context->display_graphic->xw, "A"); 5673 sprintf(buffer, "%u", state->load_alphabet); 5674 unparseputs(context->display_graphic->xw, buffer); 5675 unparseputs(context->display_graphic->xw, "\""); 5676 unparseputs(context->display_graphic->xw, state->load_name); 5677 unparseputs(context->display_graphic->xw, "\"\r"); 5678 unparse_end(context->display_graphic->xw); 5679 } 5680 break; 5681 case 'M': 5682 case 'm': 5683 TRACE(("found macrograph report \"%s\" request\n", 5684 fragment_to_tempstr(&optionarg))); 5685 { 5686 RegisDataFragment suboptionarg; 5687 char name = '\0'; 5688 5689 if (extract_regis_parenthesized_data(&optionarg, 5690 &suboptionarg)) { 5691 skip_regis_whitespace(&suboptionarg); 5692 TRACE(("got macrograph report character request: \"%s\"\n", 5693 fragment_to_tempstr(&suboptionarg))); 5694 if (!fragment_consumed(&suboptionarg)) { 5695 name = pop_fragment(&suboptionarg); 5696 if (islower(CharOf(name))) 5697 name = (char) toupper(CharOf(name)); 5698 5699 skip_regis_whitespace(&suboptionarg); 5700 if (!fragment_consumed(&optionarg)) { 5701 TRACE(("DATA_ERROR: unexpected content in ReGIS macrograph report suboptions: \"%s\"\n", 5702 fragment_to_tempstr(&suboptionarg))); 5703 break; 5704 } 5705 } 5706 } 5707 skip_regis_whitespace(&optionarg); 5708 if (!fragment_consumed(&optionarg)) { 5709 TRACE(("DATA_ERROR: unexpected arguments to ReGIS report command option '%c' arg \"%s\"\n", 5710 state->option, fragment_to_tempstr(&optionarg))); 5711 break; 5712 } 5713 5714 if (name == '\0') { 5715 TRACE(("DATA_ERROR: no macro name given to ReGIS macrograph report command\n")); 5716 break; 5717 } 5718 5719 if (name == '=') { 5720 char reply[64]; 5721 5722 TRACE(("got report macrograph storage request\n")); 5723 /* FIXME: Implement when macrographs are supported. */ 5724 sprintf(reply, "\"%u,%u\"\r", 1000U, 1000U); 5725 unparseputs(context->display_graphic->xw, reply); 5726 unparse_end(context->display_graphic->xw); 5727 } else if (name < 'A' || name > 'Z') { 5728 TRACE(("DATA_ERROR: invalid macrograph name: \"%c\"\n", name)); 5729 /* FIXME: what should happen? */ 5730 break; 5731 } else { 5732 char temp[8]; 5733 5734 TRACE(("got report macrograph request for name '%c'\n", name)); 5735 sprintf(temp, "@=%c", name); 5736 unparseputs(context->display_graphic->xw, temp); 5737 /* FIXME: Allow this to be disabled for security reasons. */ 5738 /* FIXME: implement when macrographs are supported. */ 5739 unparseputs(context->display_graphic->xw, "@;\r"); 5740 unparse_end(context->display_graphic->xw); 5741 } 5742 } 5743 break; 5744 case 'P': 5745 case 'p': 5746 TRACE(("found cursor position report \"%s\"\n", 5747 fragment_to_tempstr(&optionarg))); 5748 { 5749 RegisDataFragment suboptionarg; 5750 int output = 1; 5751 5752 if (extract_regis_parenthesized_data(&optionarg, 5753 &suboptionarg)) { 5754 skip_regis_whitespace(&suboptionarg); 5755 TRACE(("got cursor position report suboption: \"%s\"\n", 5756 fragment_to_tempstr(&suboptionarg))); 5757 if (!fragment_consumed(&suboptionarg)) { 5758 char suboption; 5759 5760 /* FIXME: handle cursor movement multipliers */ 5761 suboption = pop_fragment(&suboptionarg); 5762 if (suboption == 'i' || suboption == 'I') { 5763 output = 0; /* input location report */ 5764 } else { 5765 TRACE(("DATA_ERROR: unknown ReGIS position report suboption '%c'\n", 5766 suboption)); 5767 break; 5768 } 5769 5770 skip_regis_whitespace(&suboptionarg); 5771 if (!fragment_consumed(&optionarg)) { 5772 TRACE(("DATA_ERROR: unexpected content in ReGIS position report suboptions: \"%s\"\n", 5773 fragment_to_tempstr(&suboptionarg))); 5774 break; 5775 } 5776 } 5777 } 5778 skip_regis_whitespace(&optionarg); 5779 if (!fragment_consumed(&optionarg)) { 5780 TRACE(("DATA_ERROR: unexpected arguments to ReGIS report command option '%c' arg \"%s\"\n", 5781 state->option, fragment_to_tempstr(&optionarg))); 5782 break; 5783 } 5784 5785 TRACE(("got report cursor position (output=%d)\n", output)); 5786 5787 /* FIXME: look into supporting ANSI locator reports (DECLRP) */ 5788 if (output == 1) { 5789 char reply[64]; 5790 5791 /* FIXME: verify in absolute, not user, coordinates */ 5792 sprintf(reply, "[%d,%d]\r", 5793 context->graphics_output_cursor_x, 5794 context->graphics_output_cursor_y); 5795 unparseputs(context->display_graphic->xw, reply); 5796 unparse_end(context->display_graphic->xw); 5797 } else { 5798 char reply[64]; 5799 int x, y; 5800 5801 if (context->multi_input_mode) { 5802 /* FIXME: track input coordinates */ 5803 x = y = 0; /* placeholders */ 5804 5805 /* send CSI240~[x,y]\r with current input cursor location */ 5806 5807 /* FIXME: verify no leading char or button sequence */ 5808 /* FIXME: should we ever send an eight-bit CSI? */ 5809 /* FIXME: verify in absolute, not user, coordinates */ 5810 TRACE(("sending multi-mode input report at %d,%d\n", 5811 x, y)); 5812 sprintf(reply, "[%d,%d]\r", x, y); 5813 unparseputs(context->display_graphic->xw, reply); 5814 unparse_end(context->display_graphic->xw); 5815 break; 5816 } else { 5817 char ch; 5818 5819 /* FIXME: wait for first non-arrow keypress or mouse click, and don't update graphics while waiting */ 5820 ch = ' '; /* placeholder */ 5821 x = y = 0; /* placeholders */ 5822 5823 /* send <key or button>[x,y]\r to report input cursor location */ 5824 5825 /* null button: CSI240~ */ 5826 /* left button: CSI241~ */ 5827 /* middle button: CSI243~ */ 5828 /* right button: CSI245~ */ 5829 /* extra button: CSI247~ */ 5830 /* FIXME: support DECLBD to change button assignments */ 5831 /* FIXME: verify no leading char or button sequence */ 5832 TRACE(("sending one-shot input report with %c at %d,%d\n", 5833 ch, x, y)); 5834#if 0 /* FIXME - dead code */ 5835 if (ch == '\r') { 5836 /* Return only reports the location. */ 5837 sprintf(reply, "[%d,%d]\r", x, y); 5838 } else if (ch == '\177') { 5839 /* DEL exits locator mode reporting nothing. */ 5840 sprintf(reply, "\r"); 5841 } else 5842#endif 5843 { 5844 sprintf(reply, "%c[%d,%d]\r", ch, x, y); 5845 } 5846 unparseputs(context->display_graphic->xw, reply); 5847 unparse_end(context->display_graphic->xw); 5848 /* FIXME: exit one-shot mode and disable input cursor */ 5849 break; 5850 } 5851 } 5852 } 5853 break; 5854 default: 5855 TRACE(("DATA_ERROR: sending empty report for unknown ReGIS report command option '%c' arg \"%s\"\n", 5856 state->option, fragment_to_tempstr(&optionarg))); 5857 /* Unknown report request types must receive empty reports. */ 5858 unparseputs(context->display_graphic->xw, "\r"); 5859 unparse_end(context->display_graphic->xw); 5860 break; 5861 } 5862 break; 5863 case 's': 5864 TRACE(("inspecting screen option \"%c\" with value \"%s\"\n", 5865 state->option, fragment_to_tempstr(&optionarg))); 5866 switch (state->option) { 5867 case 'A': 5868 case 'a': 5869 TRACE(("found address definition \"%s\"\n", 5870 fragment_to_tempstr(&optionarg))); 5871 { 5872 RegisDataFragment address_extent; 5873 int got_ul = 0; 5874 int got_lr = 0; 5875 int ulx = 0, uly = 0, lrx = 0, lry = 0; 5876 5877 while (!fragment_consumed(&optionarg)) { 5878 if (skip_regis_whitespace(&optionarg)) 5879 continue; 5880 5881 if (extract_regis_extent(&optionarg, &address_extent)) { 5882 int x, y; 5883 5884 /* FIXME: are relative values supposed to be handled? */ 5885 if (!load_regis_pixel_extent(fragment_to_tempstr(&address_extent), 5886 0, 0, &x, &y)) { 5887 TRACE(("DATA_ERROR: unable to parse extent in address definition: \"%s\"\n", 5888 fragment_to_tempstr(&address_extent))); 5889 break; 5890 } 5891 5892 if (!got_ul) { 5893 ulx = x; 5894 uly = y; 5895 got_ul = 1; 5896 } else if (!got_lr) { 5897 lrx = x; 5898 lry = y; 5899 got_lr = 1; 5900 } else { 5901 TRACE(("DATA_ERROR: ignoring extra extent argument in address definition: \"%s\"\n", 5902 fragment_to_tempstr(&address_extent))); 5903 } 5904 continue; 5905 } 5906 5907 TRACE(("DATA_ERROR: ignoring malformed ReGIS screen address definition: expected extent argument but found: \"%s\"\n", 5908 fragment_to_tempstr(&optionarg))); 5909 return 1; 5910 } 5911 5912 if (!got_ul || !got_lr) { 5913 TRACE(("DATA_ERROR: ignoring malformed ReGIS screen address definition: one or both locations missing in definition\n")); 5914 return 1; 5915 } 5916 if (ulx == lrx || uly == lry) { 5917 TRACE(("DATA_ERROR: ignoring malformed ReGIS screen address definition: one or both dimensions are zero: ul=%d,%d lr=%d,%d\n", 5918 ulx, uly, lrx, lry)); 5919 return 1; 5920 } { 5921 const int cw = abs(ulx - lrx) + 1; 5922 const int ch = abs(uly - lry) + 1; 5923 int width, height; 5924 5925 /* 5926 * FIXME: Should we attempt to resize existing contents? 5927 * We are actually changing the output size, but terminals 5928 * just changed coordinates. 5929 */ 5930#if 1 5931 int scale; 5932 const int mw = context->destination_graphic->max_width; 5933 const int mh = context->destination_graphic->max_height; 5934 5935 TRACE(("custom screen size pre scaling: %dx%d\n", cw, ch)); 5936 5937 width = cw; 5938 height = ch; 5939 5940 scale = 1; 5941 while (width * scale < 200 || height * scale < 200) { 5942 scale++; 5943 } 5944 width *= scale; 5945 height *= scale; 5946 5947 scale = 1; 5948 while (width / scale > mw || height / scale > mh) { 5949 scale++; 5950 } 5951 width /= scale; 5952 height /= scale; 5953#else 5954 width = context->width; 5955 height = context->height; 5956#endif 5957 5958 TRACE(("custom screen address: ul=%d,%d lr=%d,%d\n", 5959 ulx, uly, lrx, lry)); 5960 5961 context->x_off = ulx; 5962 context->y_off = uly; 5963 context->x_div = lrx - ulx; 5964 context->y_div = lry - uly; 5965 context->width = width; 5966 context->height = height; 5967 context->destination_graphic->actual_width = width; 5968 context->destination_graphic->actual_height = height; 5969 context->destination_graphic->dirty = 1; 5970 5971 TRACE(("conversion factors: off=%+d,%+d div=%+d,%+d width=%d, height=%d\n", 5972 context->x_off, context->y_off, 5973 context->x_div, context->y_div, 5974 context->width, context->height)); 5975 } 5976 } 5977 break; 5978 case 'C': 5979 case 'c': 5980 TRACE(("found cursor control \"%s\" FIXME\n", 5981 fragment_to_tempstr(&optionarg))); 5982 /* FIXME: handle */ 5983 /* C0 == output cursor off, C1 == output cursor on */ 5984 /* C(H0) == output cursor diamond (default), C(H1) == output cursor diamond, C(H2) == output cursor crosshair */ 5985 /* C(I) == input cursor crosshair (default), C(I0) == input cursor crosshair, C(I1) == input cursor diamond, C(I2) == input cursor crosshair, C(I3) == input cursor rubber band line, C(I4) == input cursor rubber band rectangle */ 5986 /* C(I[X,Y]"FB")) == set input cursor to F in foreground B in background with hotspot at X,Y (using current text settings, trimmed to 16x24 max) */ 5987 if (fragment_consumed(&optionarg)) { 5988 TRACE(("DATA_ERROR: ignoring malformed ReGIS screen cursor control option value \"%s\"\n", 5989 fragment_to_tempstr(&optionarg))); 5990 return 1; 5991 } 5992 break; 5993 case 'E': 5994 case 'e': 5995 TRACE(("found erase request \"%s\"\n", 5996 fragment_to_tempstr(&optionarg))); 5997 if (!fragment_consumed(&optionarg)) { 5998 TRACE(("DATA_ERROR: ignoring unexpected argument to ReGIS erase request \"%s\"\n", 5999 fragment_to_tempstr(&optionarg))); 6000 return 1; 6001 } 6002 DRAW_ALL(context, context->background); 6003 /* FIXME: also set origin to 0,0 (presumably upper-left address, or maybe addressing is reset) */ 6004 context->fill_point_count = 0U; 6005 context->fill_mode = 0; 6006 state->num_points = 0U; 6007 state->stack_next = 0U; 6008 context->destination_graphic->dirty = 1; 6009 context->force_refresh = 1; 6010 break; 6011 case 'F': 6012 case 'f': 6013 TRACE(("found page eject request \"%s\"\n", 6014 fragment_to_tempstr(&optionarg))); 6015 if (!fragment_consumed(&optionarg)) { 6016 TRACE(("DATA_ERROR: ignoring unexpected argument to ReGIS page eject request \"%s\"\n", 6017 fragment_to_tempstr(&optionarg))); 6018 return 1; 6019 } 6020 /* We aren't going to print anything so no need to deduplicate. */ 6021 DRAW_ALL(context, context->background); 6022 context->destination_graphic->dirty = 1; 6023 context->force_refresh = 1; 6024 break; 6025 case 'H': 6026 case 'h': 6027 TRACE(("found hardcopy control \"%s\" FIXME\n", 6028 fragment_to_tempstr(&optionarg))); 6029 /* FIXME: handle */ 6030 /* screen S(H), area to (input?/output?) cursor S(H[X,Y]), or area S(H[X,Y][X,Y]) */ 6031 if (fragment_consumed(&optionarg)) { 6032 TRACE(("DATA_ERROR: ignoring malformed ReGIS screen hardcopy control option value \"%s\"\n", 6033 fragment_to_tempstr(&optionarg))); 6034 return 1; 6035 } 6036 break; 6037 case 'I': 6038 case 'i': 6039 TRACE(("found screen background color index \"%s\"\n", 6040 fragment_to_tempstr(&optionarg))); 6041 if (!load_regis_regnum_or_colorspec(context, &optionarg, 6042 &context->background)) { 6043 TRACE(("DATA_ERROR: screen background color specifier not recognized: \"%s\"\n", 6044 fragment_to_tempstr(&optionarg))); 6045 return 1; 6046 } 6047 break; 6048 case 'M': 6049 case 'm': 6050 TRACE(("found screen color register mapping \"%s\"\n", 6051 fragment_to_tempstr(&optionarg))); 6052 { 6053 RegisDataFragment regnum; 6054 RegisDataFragment colorspec; 6055 6056 while (!fragment_consumed(&optionarg)) { 6057 if (skip_regis_whitespace(&optionarg)) 6058 continue; 6059 6060 if (extract_regis_num(&optionarg, ®num)) { 6061 int register_num; 6062 int color_only; 6063 short r, g, b; 6064 6065 if (!regis_num_to_int(®num, ®ister_num)) { 6066 TRACE(("DATA_ERROR: unable to parse int in screen color register mapping option: \"%s\"\n", 6067 fragment_to_tempstr(®num))); 6068 return 1; 6069 } 6070 if (register_num < 0 || 6071 register_num > (int) context->destination_graphic->valid_registers) { 6072 TRACE(("interpreting out of range register number %d as 0 FIXME\n", 6073 register_num)); 6074 register_num = 0; 6075 } 6076 skip_regis_whitespace(&optionarg); 6077 if (!extract_regis_parenthesized_data(&optionarg, 6078 &colorspec)) { 6079 TRACE(("DATA_ERROR: expected to find parentheses after register number: \"%s\"\n", 6080 fragment_to_tempstr(&optionarg))); 6081 return 1; 6082 } 6083 6084 skip_regis_whitespace(&colorspec); 6085 switch (peek_fragment(&colorspec)) { 6086 case 'A': 6087 case 'a': 6088 pop_fragment(&colorspec); 6089 color_only = 1; 6090 break; 6091 default: 6092 color_only = 0; 6093 break; 6094 } 6095 6096 TRACE(("mapping register %d to color spec: \"%s\"\n", 6097 register_num, fragment_to_tempstr(&colorspec))); 6098 if (!load_regis_colorspec(context, &colorspec, 6099 &r, &g, &b)) { 6100 TRACE(("DATA_ERROR: unable to use colorspec for mapping of register %d\n", 6101 register_num)); 6102 return 1; 6103 } 6104 6105 if (color_only && 6106 (context->graphics_termid == 240 || 6107 context->graphics_termid == 330)) { 6108 TRACE(("NOT setting color register %d to %hd,%hd,%hd\n", 6109 register_num, r, g, b)); 6110 } else { 6111 TRACE(("setting color register %d to %hd,%hd,%hd\n", 6112 register_num, r, g, b)); 6113 update_color_register(context->destination_graphic, 6114 (RegisterNum) register_num, 6115 r, g, b); 6116 } 6117 continue; 6118 } { 6119 char skip; 6120 6121 skip_regis_whitespace(&optionarg); 6122 skip = pop_fragment(&optionarg); 6123 (void) skip; /* variable needed only if tracing */ 6124 TRACE(("DATA_ERROR: ignoring mapping request with unexpected character \"%c\"\n", 6125 skip)); 6126 return 1; 6127 } 6128 } 6129 } 6130 break; 6131 case 'P': 6132 case 'p': 6133 TRACE(("found graphics page display option \"%s\"\n", 6134 fragment_to_tempstr(&optionarg))); 6135 { 6136 RegisDataFragment pagearg; 6137 int page; 6138 6139 if (!extract_regis_num(&optionarg, &pagearg)) { 6140 TRACE(("DATA_ERROR: expected int in page display option: \"%s\"\n", 6141 fragment_to_tempstr(&optionarg))); 6142 break; 6143 } 6144 TRACE(("page option arg: %s\n", fragment_to_tempstr(&pagearg))); 6145 if (!regis_num_to_int(&pagearg, &page)) { 6146 TRACE(("DATA_ERROR: unable to parse int in page display option: \"%s\"\n", 6147 fragment_to_tempstr(&pagearg))); 6148 break; 6149 } 6150 if (page < 0 || (unsigned) page >= MAX_REGIS_PAGES) { 6151 TRACE(("DATA_ERROR: invalid page: \"%d\"\n", page)); 6152 break; 6153 } 6154 6155 TRACE(("using display page number: %d\n", page)); 6156 context->display_page = (unsigned) page; 6157 map_regis_graphics_pages(context->display_graphic->xw, context); 6158 } 6159 break; 6160 case 'T': 6161 case 't': 6162 TRACE(("found time delay \"%s\" FIXME\n", 6163 fragment_to_tempstr(&optionarg))); 6164 { 6165 RegisDataFragment delayarg; 6166 int delay; 6167 6168 if (!extract_regis_num(&optionarg, &delayarg)) { 6169 TRACE(("DATA_ERROR: expected int in delay display option: \"%s\"\n", 6170 fragment_to_tempstr(&optionarg))); 6171 break; 6172 } 6173 TRACE(("delay option arg: %s\n", 6174 fragment_to_tempstr(&delayarg))); 6175 if (!regis_num_to_int(&delayarg, &delay)) { 6176 TRACE(("DATA_ERROR: unable to parse int in delay display option: \"%s\"\n", 6177 fragment_to_tempstr(&delayarg))); 6178 break; 6179 } 6180 if (delay < 0 || delay > MAX_I_DELAY) { 6181 TRACE(("DATA_ERROR: delay out of range: \"%d\"\n", delay)); 6182 break; 6183 } 6184 /* FIXME: terminals allow much larger values, but this is an easy DoS vector */ 6185 if (delay > 60) 6186 delay = 60; 6187 TRACE(("using delay for %d ticks\n", delay)); 6188 refresh_modified_displayed_graphics(context->current_widget); 6189 usleep((useconds_t) delay * 10000U); 6190 } 6191 break; 6192 case 'W': 6193 case 'w': 6194 /* Only M (pixel vector multiplier) is useful -- for the scrolling multiplier. */ 6195 TRACE(("found temporary write options \"%s\"\n", 6196 fragment_to_tempstr(&optionarg))); 6197 if (!load_regis_write_control_set(state, context, 6198 context->graphics_output_cursor_x, 6199 context->graphics_output_cursor_y, 6200 &optionarg, 6201 &context->temporary_write_controls)) { 6202 TRACE(("DATA_ERROR: invalid temporary write options \"%s\"\n", 6203 fragment_to_tempstr(&optionarg))); 6204 break; 6205 } 6206 break; 6207 default: 6208 TRACE(("DATA_ERROR: ignoring unknown ReGIS screen command option '%c' arg \"%s\"\n", 6209 state->option, fragment_to_tempstr(&optionarg))); 6210 break; 6211 } 6212 break; 6213 case 't': 6214 TRACE(("inspecting text option \"%c\" with value \"%s\"\n", 6215 state->option, fragment_to_tempstr(&optionarg))); 6216 switch (state->option) { 6217 case 'A': 6218 case 'a': 6219 TRACE(("found alphabet specifier option \"%s\"\n", 6220 fragment_to_tempstr(&optionarg))); 6221 { 6222 RegisDataFragment alphabetarg; 6223 int alphabet; 6224 6225 if (!extract_regis_num(&optionarg, &alphabetarg)) { 6226 TRACE(("DATA_ERROR: expected int in text alphabet option: \"%s\"\n", 6227 fragment_to_tempstr(&optionarg))); 6228 break; 6229 } 6230 TRACE(("alphabet: %s\n", fragment_to_tempstr(&alphabetarg))); 6231 if (!regis_num_to_int(&alphabetarg, &alphabet)) { 6232 TRACE(("DATA_ERROR: unable to parse int in text alphabet option: \"%s\"\n", 6233 fragment_to_tempstr(&alphabetarg))); 6234 break; 6235 } 6236 if (alphabet < 0 || 6237 (unsigned) alphabet >= MAX_REGIS_ALPHABETS) { 6238 TRACE(("DATA_ERROR: invalid alphabet: \"%d\"\n", alphabet)); 6239 break; 6240 } 6241 6242 TRACE(("using alphabet number: %d\n", alphabet)); 6243 context->current_text_controls->alphabet_num = 6244 (unsigned) alphabet; 6245 6246 skip_regis_whitespace(&optionarg); 6247 if (!fragment_consumed(&optionarg)) { 6248 TRACE(("DATA_ERROR: ignoring trailing junk in text alphabet option \"%s\"\n", 6249 fragment_to_tempstr(&alphabetarg))); 6250 break; 6251 } 6252 } 6253 break; 6254 case 'B': 6255 case 'b': 6256 TRACE(("found beginning of temporary text control \"%s\"\n", 6257 fragment_to_tempstr(&optionarg))); 6258 copy_regis_text_controls(&context->persistent_text_controls, 6259 &context->temporary_text_controls); 6260 context->current_text_controls = &context->temporary_text_controls; 6261 break; 6262 case 'D': 6263 case 'd': 6264 TRACE(("found text tilt control \"%s\"\n", 6265 fragment_to_tempstr(&optionarg))); 6266 { 6267 RegisDataFragment rotationarg; 6268 int rotation; 6269 6270 if (!extract_regis_num(&optionarg, &rotationarg)) { 6271 TRACE(("DATA_ERROR: expected int in text tilt option: \"%s\"\n", 6272 fragment_to_tempstr(&optionarg))); 6273 break; 6274 } 6275 TRACE(("tilt: %s\n", fragment_to_tempstr(&rotationarg))); 6276 if (!regis_num_to_int(&rotationarg, &rotation)) { 6277 TRACE(("DATA_ERROR: unable to parse int in text tilt option: \"%s\"\n", 6278 fragment_to_tempstr(&rotationarg))); 6279 break; 6280 } 6281 while (rotation < 0) { 6282 rotation += 360; 6283 } 6284 while (rotation >= 360) { 6285 rotation -= 360; 6286 } 6287 /* FIXME: we don't have to be this restrictive, though the 6288 * VT3x0 apparently was. What might depend on this? 6289 */ 6290#ifndef ENABLE_FREE_ROTATION 6291 /* Use closest value which is a multiple of 45 degrees. */ 6292 rotation = 45 * ((rotation + 22) / 45); 6293#endif 6294 6295 /* For some reason ReGIS reused the "D" option for the text 6296 * command to represent two different attributes. String tilt 6297 * can only be modified if an "S" option is given after the 6298 * "D" option. In that case a second "D" option changes the 6299 * character tilt. But complicating things further, if no "S" 6300 * or second "D" option is given, the "D" option refers to the 6301 * character tilt. 6302 */ 6303 switch (state->text_tilt_state) { 6304 case TEXT_TILT_STATE_READY: 6305 /* Setting a tilt after a cell size only affects the 6306 * character tilt and not the string tilt. 6307 */ 6308 TRACE(("character rotation (direction): %d\n", rotation)); 6309 context->current_text_controls->character_rotation 6310 = rotation; 6311 state->text_tilt_state = TEXT_TILT_STATE_GOT_D; 6312 break; 6313 case TEXT_TILT_STATE_GOT_D: 6314 /* If there are multiple angles with no size only the last 6315 * value is used. 6316 */ 6317 TRACE(("character rotation (direction): %d\n", rotation)); 6318 context->current_text_controls->character_rotation 6319 = rotation; 6320 break; 6321 case TEXT_TILT_STATE_GOT_DS: 6322 TRACE(("changing character rotation (direction): %d\n", 6323 rotation)); 6324 context->current_text_controls->character_rotation 6325 = rotation; 6326 state->text_tilt_state = TEXT_TILT_STATE_GOT_DSD; 6327 break; 6328 case TEXT_TILT_STATE_GOT_DSD: 6329 default: 6330 /* If there are multiple angles with no size only the last 6331 * value is used. 6332 */ 6333 TRACE(("changing character rotation (direction): %d\n", 6334 rotation)); 6335 context->current_text_controls->character_rotation 6336 = rotation; 6337 break; 6338 } 6339 6340 skip_regis_whitespace(&optionarg); 6341 if (!fragment_consumed(&optionarg)) { 6342 TRACE(("DATA_ERROR: ignoring trailing junk in text tilt option \"%s\"\n", 6343 fragment_to_tempstr(&rotationarg))); 6344 break; 6345 } 6346 } 6347 break; 6348 case 'E': 6349 case 'e': 6350 TRACE(("found end of temporary text control \"%s\"\n", 6351 fragment_to_tempstr(&optionarg))); 6352 context->current_text_controls = &context->persistent_text_controls; 6353 break; 6354 case 'H': 6355 case 'h': 6356 TRACE(("found height multiplier \"%s\"\n", 6357 fragment_to_tempstr(&optionarg))); 6358 { 6359 RegisDataFragment multiarg; 6360 int multiplier; 6361 unsigned height; 6362 6363 if (!extract_regis_num(&optionarg, &multiarg)) { 6364 TRACE(("DATA_ERROR: expected int in text height multiplier option: \"%s\"\n", 6365 fragment_to_tempstr(&optionarg))); 6366 break; 6367 } 6368 TRACE(("multiplier: %s\n", fragment_to_tempstr(&multiarg))); 6369 if (!regis_num_to_int(&multiarg, &multiplier)) { 6370 TRACE(("DATA_ERROR: unable to parse int in text height multiplier option: \"%s\"\n", 6371 fragment_to_tempstr(&multiarg))); 6372 break; 6373 } 6374 if (multiplier < 0) { 6375 TRACE(("DATA_ERROR: out of range height multiplier: \"%d\", using 0 FIXME\n", 6376 multiplier)); 6377 multiplier = 0; /* FIXME: verify zero is accepted */ 6378 } 6379 if (multiplier > 256) { 6380 TRACE(("DATA_ERROR: out of range height multiplier: \"%d\", using 256 FIXME\n", 6381 multiplier)); 6382 multiplier = 256; 6383 } 6384 TRACE(("using height multiplier: %d\n", multiplier)); 6385 height = (unsigned) multiplier *10U; /* base character height */ 6386 context->current_text_controls->character_display_h 6387 = height; 6388 context->current_text_controls->character_unit_cell_h 6389 = height; 6390 6391 skip_regis_whitespace(&optionarg); 6392 if (!fragment_consumed(&optionarg)) { 6393 TRACE(("DATA_ERROR: ignoring trailing junk in text multiplier option \"%s\"\n", 6394 fragment_to_tempstr(&multiarg))); 6395 break; 6396 } 6397 } 6398 break; 6399 case 'I': 6400 case 'i': 6401 TRACE(("found italic control \"%s\"\n", 6402 fragment_to_tempstr(&optionarg))); 6403 { 6404 RegisDataFragment italicarg; 6405 int italic; 6406 6407 if (!extract_regis_num(&optionarg, &italicarg)) { 6408 TRACE(("DATA_ERROR: expected int in text italic option: \"%s\"\n", 6409 fragment_to_tempstr(&optionarg))); 6410 break; 6411 } 6412 TRACE(("italic angle: %s\n", fragment_to_tempstr(&italicarg))); 6413 if (!regis_num_to_int(&italicarg, &italic)) { 6414 TRACE(("DATA_ERROR: unable to parse int in text italic option: \"%s\"\n", 6415 fragment_to_tempstr(&italicarg))); 6416 break; 6417 } 6418 6419 /* 6420 * This is overly-restrictive but matches what the docs say 6421 * should happen. Add an option to allow exact angles? 6422 */ 6423#ifndef ENABLE_VARIABLE_ITALICS 6424 if (italic <= -31) { 6425 italic = -45; 6426 } else if (italic < 0) { 6427 italic = -27; /* docs say 22, but that gives .404 x:y ratio */ 6428 } else if (italic >= 31) { 6429 italic = 45; 6430 } else if (italic > 0) { 6431 italic = 27; /* docs say 22, but that gives .404 x:y ratio */ 6432 } 6433#else 6434 if (italic <= -72) { 6435 italic = -72; 6436 } else if (italic >= 72) { 6437 italic = 72; 6438 } 6439#endif 6440 6441 TRACE(("using italic angle: %d\n", italic)); 6442 context->current_text_controls->slant = italic; 6443 6444 skip_regis_whitespace(&optionarg); 6445 if (!fragment_consumed(&optionarg)) { 6446 TRACE(("DATA_ERROR: ignoring trailing junk in text italic option \"%s\"\n", 6447 fragment_to_tempstr(&italicarg))); 6448 break; 6449 } 6450 } 6451 break; 6452 case 'M': 6453 case 'm': 6454 TRACE(("found text command unit size multiplier \"%s\"\n", 6455 fragment_to_tempstr(&optionarg))); 6456 { 6457 RegisDataFragment sizemultiplierarg; 6458 int sizemultiplier; 6459 int ww, hh; 6460 6461 if (!extract_regis_extent(&optionarg, &sizemultiplierarg)) { 6462 TRACE(("DATA_ERROR: expected extent in unit size multiplier option: \"%s\"\n", 6463 fragment_to_tempstr(&optionarg))); 6464 break; 6465 } 6466 TRACE(("size multiplier: %s\n", 6467 fragment_to_tempstr(&sizemultiplierarg))); 6468 /* FIXME: verify that relative values don't work */ 6469 if (!load_regis_mult_extent(fragment_to_tempstr(&sizemultiplierarg), 6470 &ww, &hh)) { 6471 TRACE(("DATA_ERROR: unable to parse extent in '%c' command: \"%s\"\n", 6472 state->option, fragment_to_tempstr(&sizemultiplierarg))); 6473 break; 6474 } 6475 /* FIXME: support fractional values */ 6476 if (!regis_num_to_int(&sizemultiplierarg, &sizemultiplier)) { 6477 TRACE(("DATA_ERROR: unable to parse extent in size multiplier option: \"%s\"\n", 6478 fragment_to_tempstr(&sizemultiplierarg))); 6479 break; 6480 } 6481 if (ww < 1 || hh < 1) { 6482 TRACE(("DATA_ERROR: invalid size multiplier: %d,%d FIXME\n", 6483 ww, hh)); 6484 break; 6485 } 6486 if (ww > 16) { 6487 ww = 16; 6488 } 6489 if (hh > 16) { 6490 hh = 16; 6491 } 6492 6493 TRACE(("using unit size multiplier: %d,%d\n", ww, hh)); 6494 6495 /* Times the base character unit cell dimensions (the VT3x0 6496 * manual says this is with the height multiplier, but that 6497 * seems to be incorrect). 6498 */ 6499 context->current_text_controls->character_unit_cell_w = 6500 (unsigned) ww *8U; 6501 context->current_text_controls->character_unit_cell_h = 6502 (unsigned) hh *10U; 6503 6504 skip_regis_whitespace(&optionarg); 6505 if (!fragment_consumed(&optionarg)) { 6506 TRACE(("DATA_ERROR: ignoring trailing junk in text unit cell size option \"%s\"\n", 6507 fragment_to_tempstr(&sizemultiplierarg))); 6508 break; 6509 } 6510 } 6511 break; 6512 case 'S': 6513 case 's': 6514 TRACE(("found display size or standard character cell size \"%s\"\n", 6515 fragment_to_tempstr(&optionarg))); 6516 for (;;) { 6517 RegisDataFragment displaysizearg; 6518 6519 skip_regis_whitespace(&optionarg); 6520 if (extract_regis_extent(&optionarg, &displaysizearg)) { 6521 int disp_w, disp_h; 6522 6523 TRACE(("custom display size: %s\n", 6524 fragment_to_tempstr(&displaysizearg))); 6525 /* FIXME: verify that relative values don't work */ 6526 if (!load_regis_mult_extent(fragment_to_tempstr(&displaysizearg), 6527 &disp_w, &disp_h)) { 6528 TRACE(("DATA_ERROR: unable to parse extent in text display size option: \"%s\"\n", 6529 fragment_to_tempstr(&displaysizearg))); 6530 break; 6531 } 6532 if (disp_w < 1 || disp_h < 1) { 6533 TRACE(("DATA_ERROR: invalid text display size: %dx%d FIXME\n", 6534 disp_w, disp_h)); 6535 break; 6536 } 6537 6538 TRACE(("using display cell size: %d,%d\n", disp_w, disp_h)); 6539 context->current_text_controls->character_display_w = 6540 (unsigned) disp_w; 6541 context->current_text_controls->character_display_h = 6542 (unsigned) disp_h; 6543 TRACE(("using offset: %d,%d\n", disp_w, 0)); 6544 context->current_text_controls->character_inc_x = disp_w; 6545 context->current_text_controls->character_inc_y = 0; 6546 6547 continue; 6548 } 6549 6550 if (extract_regis_num(&optionarg, &displaysizearg)) { 6551 int standard; 6552 unsigned disp_w, disp_h, unit_w, unit_h; 6553 int off_x, off_y; 6554 6555 TRACE(("standard display cell size: %s\n", 6556 fragment_to_tempstr(&displaysizearg))); 6557 if (!regis_num_to_int(&displaysizearg, &standard)) { 6558 TRACE(("DATA_ERROR: unable to parse int in text standard cell size option: \"%s\"\n", 6559 fragment_to_tempstr(&displaysizearg))); 6560 break; 6561 } 6562 if (get_standard_character_size(standard, 6563 &disp_w, &disp_h, 6564 &unit_w, &unit_h, 6565 &off_x, &off_y)) { 6566 TRACE(("DATA_ERROR: unrecognized standard cell size: \"%d\"\n", 6567 standard)); 6568 break; 6569 } 6570 6571 TRACE(("using display cell size: %u,%u\n", disp_w, disp_h)); 6572 context->current_text_controls->character_display_w 6573 = disp_w; 6574 context->current_text_controls->character_display_h 6575 = disp_h; 6576 TRACE(("using offset: %d,%d\n", off_x, off_y)); 6577 context->current_text_controls->character_inc_x = off_x; 6578 context->current_text_controls->character_inc_y = off_y; 6579 6580 /* 6581 * Some ReGIS documentation implies that the "S" option only 6582 * affects character spacing after a rotation option ("ReGIS 6583 * uses the spacing value associated with the cell size to 6584 * space the characters in the tilted string"). The 7-13 6585 * example in the VT330/VT340 Programmer Reference Manual 6586 * vol 2 appears to say otherwise. FIXME: verify 6587 */ 6588 if (1) { /* forced for now */ 6589 TRACE(("using unit cell size: %u,%u\n", unit_w, unit_h)); 6590 context->current_text_controls->character_unit_cell_w = 6591 unit_w; 6592 context->current_text_controls->character_unit_cell_h = 6593 unit_h; 6594 } 6595 6596 switch (state->text_tilt_state) { 6597 case TEXT_TILT_STATE_READY: 6598 /* Nothing to do. */ 6599 break; 6600 case TEXT_TILT_STATE_GOT_D: 6601 TRACE(("upgrading character rotation to string and character rotation: %d\n", context->current_text_controls->character_rotation)); 6602 context->current_text_controls->string_rotation 6603 = context->current_text_controls->character_rotation; 6604 state->text_tilt_state = TEXT_TILT_STATE_GOT_DS; 6605 break; 6606 case TEXT_TILT_STATE_GOT_DS: 6607 /* FIXME: It isn't clear what to do if there are two 6608 * size options in a row after a tilt option. 6609 */ 6610 TRACE(("DATA_ERROR: unexpected duplicate size option: \"%s\" (state=%d)\n", 6611 fragment_to_tempstr(&displaysizearg), 6612 state->text_tilt_state)); 6613 break; 6614 case TEXT_TILT_STATE_GOT_DSD: 6615 default: 6616 /* FIXME: It isn't clear what to do if there is a size 6617 * option after both types of tilt angle have been set. 6618 */ 6619 TRACE(("DATA_ERROR: unexpected duplicate size option: \"%s\" (state=%d)\n", 6620 fragment_to_tempstr(&displaysizearg), 6621 state->text_tilt_state)); 6622 break; 6623 } 6624 continue; 6625 } 6626 6627 if (skip_regis_whitespace(&optionarg)) { 6628 continue; 6629 } 6630 6631 if (fragment_consumed(&optionarg)) { 6632 break; 6633 } 6634 6635 TRACE(("DATA_ERROR: expected int or extent in text display size option: \"%s\"\n", 6636 fragment_to_tempstr(&optionarg))); 6637 break; 6638 } 6639 break; 6640 case 'U': 6641 case 'u': 6642 TRACE(("found text command custom unit cell size \"%s\"\n", 6643 fragment_to_tempstr(&optionarg))); 6644 { 6645 RegisDataFragment unitsizearg; 6646 int unitsize; 6647 int unit_w, unit_h; 6648 6649 if (!extract_regis_extent(&optionarg, &unitsizearg)) { 6650 TRACE(("DATA_ERROR: expected extent in text unit cell size option: \"%s\"\n", 6651 fragment_to_tempstr(&optionarg))); 6652 break; 6653 } 6654 TRACE(("unitsize cell size: %s\n", 6655 fragment_to_tempstr(&unitsizearg))); 6656 /* FIXME: verify that relative values don't work */ 6657 if (!load_regis_mult_extent(fragment_to_tempstr(&unitsizearg), 6658 &unit_w, &unit_h)) { 6659 TRACE(("DATA_ERROR: unable to parse extent in '%c' command: \"%s\"\n", 6660 state->option, fragment_to_tempstr(&unitsizearg))); 6661 break; 6662 } 6663 if (!regis_num_to_int(&unitsizearg, &unitsize)) { 6664 TRACE(("DATA_ERROR: unable to parse extent in text unit cell size option: \"%s\"\n", 6665 fragment_to_tempstr(&unitsizearg))); 6666 break; 6667 } 6668 if (unit_w < 1 || unit_h < 1) { 6669 TRACE(("DATA_ERROR: invalid text unit cell size: %dx%d FIXME\n", 6670 unit_w, unit_h)); 6671 break; 6672 } 6673 6674 TRACE(("using unit cell size: %d,%d\n", unit_w, unit_h)); 6675 6676 context->current_text_controls->character_unit_cell_w = 6677 (unsigned) unit_w; 6678 context->current_text_controls->character_unit_cell_h = 6679 (unsigned) unit_h; 6680 6681 skip_regis_whitespace(&optionarg); 6682 if (!fragment_consumed(&optionarg)) { 6683 TRACE(("DATA_ERROR: ignoring trailing junk in text unit cell size option \"%s\"\n", 6684 fragment_to_tempstr(&unitsizearg))); 6685 break; 6686 } 6687 } 6688 break; 6689 case 'W': 6690 case 'w': 6691 TRACE(("found temporary write options \"%s\"\n", 6692 fragment_to_tempstr(&optionarg))); 6693 if (!load_regis_write_control_set(state, context, 6694 context->graphics_output_cursor_x, 6695 context->graphics_output_cursor_y, 6696 &optionarg, 6697 &context->temporary_write_controls)) { 6698 TRACE(("DATA_ERROR: invalid temporary write options \"%s\"\n", 6699 fragment_to_tempstr(&optionarg))); 6700 break; 6701 } 6702 break; 6703 default: 6704 TRACE(("DATA_ERROR: ignoring unknown ReGIS text command option '%c' arg \"%s\"\n", 6705 state->option, fragment_to_tempstr(&optionarg))); 6706 break; 6707 } 6708 break; 6709 case 'v': 6710 TRACE(("inspecting vector option \"%c\" with value \"%s\"\n", 6711 state->option, fragment_to_tempstr(&optionarg))); 6712 switch (state->option) { 6713 case 'B': 6714 case 'b': 6715 TRACE(("found begin bounded position stack \"%s\"\n", 6716 fragment_to_tempstr(&optionarg))); 6717 skip_regis_whitespace(&optionarg); 6718 if (!fragment_consumed(&optionarg)) { 6719 TRACE(("DATA_ERROR: ignoring unexpected arguments to vector option '%c' arg \"%s\"\n", 6720 state->option, fragment_to_tempstr(&optionarg))); 6721 } 6722 if (state->stack_next >= POSITION_STACK_SIZE) { 6723 /* FIXME: ignore, error, update counter? */ 6724 TRACE(("unable to push position to full stack\n")); 6725 break; 6726 } 6727 TRACE(("pushing location: %d,%d\n", 6728 context->graphics_output_cursor_x, 6729 context->graphics_output_cursor_y)); 6730 6731 state->stack_x[state->stack_next] = 6732 context->graphics_output_cursor_x; 6733 state->stack_y[state->stack_next] = 6734 context->graphics_output_cursor_y; 6735 state->stack_next++; 6736 break; 6737 case 'E': 6738 case 'e': 6739 TRACE(("found end vector position stack \"%s\"\n", 6740 fragment_to_tempstr(&optionarg))); 6741 skip_regis_whitespace(&optionarg); 6742 if (!fragment_consumed(&optionarg)) { 6743 TRACE(("DATA_ERROR: ignoring unexpected arguments to vector option '%c' arg \"%s\"\n", 6744 state->option, fragment_to_tempstr(&optionarg))); 6745 } 6746 if (state->stack_next == 0U) { 6747 TRACE(("DATA_ERROR: unable to pop position from empty stack\n")); 6748 break; 6749 } 6750 6751 state->stack_next--; 6752 if (state->stack_x[state->stack_next] != DUMMY_STACK_X || 6753 state->stack_y[state->stack_next] != DUMMY_STACK_Y) { 6754 int orig_x, orig_y; 6755 6756 orig_x = context->graphics_output_cursor_x; 6757 orig_y = context->graphics_output_cursor_y; 6758 context->graphics_output_cursor_x = 6759 state->stack_x[state->stack_next]; 6760 context->graphics_output_cursor_y = 6761 state->stack_y[state->stack_next]; 6762 TRACE(("popped location: %d,%d\n", 6763 context->graphics_output_cursor_x, 6764 context->graphics_output_cursor_y)); 6765 6766 TRACE(("drawing line to popped location\n")); 6767 draw_patterned_line(context, 6768 orig_x, orig_y, 6769 context->graphics_output_cursor_x, 6770 context->graphics_output_cursor_y); 6771 } else { 6772 TRACE(("not popping location\n")); 6773 } 6774 break; 6775 case 'S': 6776 case 's': 6777 TRACE(("found begin unbounded position stack \"%s\"\n", 6778 fragment_to_tempstr(&optionarg))); 6779 skip_regis_whitespace(&optionarg); 6780 if (!fragment_consumed(&optionarg)) { 6781 TRACE(("DATA_ERROR: ignoring unexpected arguments to vector option '%c' arg \"%s\"\n", 6782 state->option, fragment_to_tempstr(&optionarg))); 6783 } 6784 if (state->stack_next >= POSITION_STACK_SIZE) { 6785 /* FIXME: ignore, error, update counter? */ 6786 TRACE(("unable to push dummy position to full stack\n")); 6787 break; 6788 } 6789 6790 TRACE(("pushing dummy vector positions instead of %d,%d\n", 6791 context->graphics_output_cursor_x, 6792 context->graphics_output_cursor_y)); 6793 state->stack_x[state->stack_next] = DUMMY_STACK_X; 6794 state->stack_y[state->stack_next] = DUMMY_STACK_Y; 6795 state->stack_next++; 6796 break; 6797 case 'W': 6798 case 'w': 6799 TRACE(("found temporary write options \"%s\"\n", 6800 fragment_to_tempstr(&optionarg))); 6801 if (!load_regis_write_control_set(state, context, 6802 context->graphics_output_cursor_x, 6803 context->graphics_output_cursor_y, 6804 &optionarg, 6805 &context->temporary_write_controls)) { 6806 TRACE(("DATA_ERROR: invalid temporary write options \"%s\"\n", 6807 fragment_to_tempstr(&optionarg))); 6808 } 6809 break; 6810 default: 6811 TRACE(("DATA_ERROR: ignoring unknown ReGIS vector command option '%c' arg \"%s\"\n", 6812 state->option, fragment_to_tempstr(&optionarg))); 6813 break; 6814 } 6815 break; 6816 case 'w': 6817 skip_regis_whitespace(&optionarg); 6818 TRACE(("inspecting permanent write option \"%c\" with value \"%s\"\n", 6819 state->option, fragment_to_tempstr(&optionarg))); 6820 if (!load_regis_write_control(state, context, 6821 context->graphics_output_cursor_x, 6822 context->graphics_output_cursor_y, 6823 state->option, &optionarg, 6824 &context->persistent_write_controls)) { 6825 TRACE(("DATA_ERROR: invalid write options\n")); 6826 return 1; 6827 } 6828 break; 6829 default: 6830 TRACE(("DATA_ERROR: unexpected option in \"%c\" command: \"%s\"\n", 6831 state->command, fragment_to_tempstr(&optionarg))); 6832 return 1; 6833 } 6834 6835 return 1; 6836} 6837 6838static int 6839expand_macrographs(RegisDataFragment *input, RegisGraphicsContext const *context) 6840{ 6841 char operator; 6842 char name; 6843 6844 (void) context; /* to be used later */ 6845 operator = get_fragment(input, 0U); 6846 if (operator != '@') 6847 return 0; 6848 name = get_fragment(input, 1U); 6849 if (islower(CharOf(name))) 6850 name = (char) toupper(CharOf(name)); 6851 if (name < 'A' || name > 'Z') 6852 return 0; 6853 6854 TRACE(("expanding macrograph '%c' with %u characters FIXME\n", name, 0U)); 6855 pop_fragment(input); 6856 pop_fragment(input); 6857 /* FIXME: implement */ 6858 return 1; 6859} 6860 6861static int 6862parse_regis_items(RegisParseState *state, RegisGraphicsContext *context) 6863{ 6864 RegisDataFragment *const input = &state->input; 6865 RegisDataFragment item; 6866 6867 if (fragment_consumed(input)) 6868 return 0; 6869 6870 if (extract_regis_extent(input, &item)) { 6871 TRACE(("found extent \"%s\"\n", fragment_to_tempstr(&item))); 6872 switch (state->command) { 6873 case 'c': 6874 { 6875 int orig_x, orig_y; 6876 int new_x, new_y; 6877 6878 if (state->num_points > 0) { 6879 orig_x = state->x_points[state->num_points - 1]; 6880 orig_y = state->y_points[state->num_points - 1]; 6881 } else { 6882 orig_x = context->graphics_output_cursor_x; 6883 orig_y = context->graphics_output_cursor_y; 6884 } 6885 if (!load_regis_coord_extent(context, 6886 fragment_to_tempstr(&item), 6887 orig_x, orig_y, 6888 &new_x, &new_y)) { 6889 TRACE(("DATA_ERROR: unable to parse extent in '%c' command: \"%s\"\n", 6890 state->command, fragment_to_tempstr(&item))); 6891 break; 6892 } 6893 6894 switch (state->curve_mode) { 6895 case CURVE_POSITION_ARC_CENTER: 6896 case CURVE_POSITION_ARC_EDGE: 6897 { 6898 double radians; 6899 int tenthdegs; 6900 int c_x, c_y; 6901 int e_x, e_y; 6902 int e_x_final = 0, e_y_final = 0; 6903 6904 TRACE(("drawing arc: curve_mode=%d\n", state->curve_mode)); 6905 TRACE(("drawing arc: new=%d,%d orig=%d,%d\n", 6906 new_x, new_y, orig_x, orig_y)); 6907 if (state->curve_mode == CURVE_POSITION_ARC_CENTER) { 6908 c_x = new_x; 6909 c_y = new_y; 6910 e_x = orig_x; 6911 e_y = orig_y; 6912 } else { 6913 c_x = orig_x; 6914 c_y = orig_y; 6915 e_x = new_x; 6916 e_y = new_y; 6917 } 6918 6919 radians = atan2((double) (c_y - e_y), 6920 (double) (e_x - c_x)); 6921 tenthdegs = (int) (0.5 + 3600.0 * radians / (2.0 * M_PI)); 6922 if (tenthdegs < 0) 6923 tenthdegs += 3600; 6924 6925 TRACE(("drawing arc centered at location %d,%d to location %d,%d from %g degrees (%g radians) for %d degrees\n", 6926 c_x, c_y, 6927 e_x, e_y, 6928 tenthdegs / 10., radians, state->arclen)); 6929 draw_patterned_arc(context, 6930 c_x, c_y, 6931 e_x, e_y, 6932 tenthdegs, state->arclen * 10, 6933 &e_x_final, &e_y_final); 6934 6935#ifdef DEBUG_ARC_CENTER 6936 DRAW_PIXEL(context, c_x + 1, c_y, 3U); 6937 DRAW_PIXEL(context, c_x - 1, c_y, 3U); 6938 DRAW_PIXEL(context, c_x, c_y + 1, 3U); 6939 DRAW_PIXEL(context, c_x, c_y - 1, 3U); 6940 DRAW_PIXEL(context, c_x, c_y, 3U); 6941#endif 6942 6943#ifdef DEBUG_ARC_START 6944 DRAW_PIXEL(context, e_x + 1, e_y, 2U); 6945 DRAW_PIXEL(context, e_x - 1, e_y, 2U); 6946 DRAW_PIXEL(context, e_x, e_y + 1, 2U); 6947 DRAW_PIXEL(context, e_x, e_y - 1, 2U); 6948 DRAW_PIXEL(context, e_x, e_y, 2U); 6949#endif 6950 6951#ifdef DEBUG_ARC_END 6952 DRAW_PIXEL(context, e_x_final + 1, e_y_final + 1, 1U); 6953 DRAW_PIXEL(context, e_x_final + 1, e_y_final - 1, 1U); 6954 DRAW_PIXEL(context, e_x_final - 1, e_y_final + 1, 1U); 6955 DRAW_PIXEL(context, e_x_final - 1, e_y_final - 1, 1U); 6956 DRAW_PIXEL(context, e_x_final, e_y_final, 1U); 6957#endif 6958 6959 if (state->curve_mode == CURVE_POSITION_ARC_CENTER) { 6960 TRACE(("moving cursor to final point on arc %d,%d\n", 6961 e_x_final, e_y_final)); 6962 if (state->num_points > 0) { 6963 state->x_points[state->num_points - 1] = 6964 e_x_final; 6965 state->y_points[state->num_points - 1] = 6966 e_y_final; 6967 } 6968 context->graphics_output_cursor_x = e_x_final; 6969 context->graphics_output_cursor_y = e_y_final; 6970 } 6971 } 6972 break; 6973 case CURVE_POSITION_OPEN_CURVE: 6974 case CURVE_POSITION_CLOSED_CURVE: 6975 if (state->num_points >= MAX_INPUT_CURVE_POINTS) { 6976 TRACE(("DATA_ERROR: got curve point, but already have max points (%d)\n", 6977 state->num_points)); 6978 break; 6979 } 6980 state->x_points[state->num_points] = new_x; 6981 state->y_points[state->num_points] = new_y; 6982 state->num_points++; 6983 TRACE(("adding point to curve with location %d,%d\n", 6984 new_x, new_y)); 6985 break; 6986 default: 6987 TRACE(("ERROR: got position, but curve mode %d is unknown\n", 6988 state->curve_mode)); 6989 break; 6990 } 6991 } 6992 break; 6993 case 'p': 6994 if (!load_regis_coord_extent(context, 6995 fragment_to_tempstr(&item), 6996 context->graphics_output_cursor_x, 6997 context->graphics_output_cursor_y, 6998 &context->graphics_output_cursor_x, 6999 &context->graphics_output_cursor_y)) { 7000 TRACE(("DATA_ERROR: unable to parse extent in '%c' command: \"%s\"\n", 7001 state->command, fragment_to_tempstr(&item))); 7002 break; 7003 } 7004 TRACE(("moving pen to location %d,%d\n", 7005 context->graphics_output_cursor_x, 7006 context->graphics_output_cursor_y)); 7007 break; 7008 case 's': 7009 TRACE(("extent scroll argument to screen command: \"%s\"\n", 7010 fragment_to_tempstr(&item))); 7011 { 7012 int old_ul_x, old_ul_y; 7013 int new_ul_x, new_ul_y; 7014 int copy_w, copy_h; 7015 7016 old_ul_x = 0; 7017 old_ul_y = 0; 7018 TRACE(("current upper-left coordinate in pixel coordinates: %d,%d\n", 7019 old_ul_x, old_ul_y)); 7020 /* FIXME: verify this is in user coordinates, not pixels */ 7021 if (!load_regis_coord_extent(context, 7022 fragment_to_tempstr(&item), 7023 old_ul_x, old_ul_y, 7024 &new_ul_x, &new_ul_y)) { 7025 TRACE(("DATA_ERROR: unable to parse extent in '%c' command: \"%s\"\n", 7026 state->command, fragment_to_tempstr(&item))); 7027 break; 7028 } 7029 TRACE(("scrolling image to updated upper-left coordinate in pixel coordinates: %d,%d\n", 7030 new_ul_x, new_ul_y)); 7031 7032 /* FIXME: does any write mode affect revealed background? */ 7033 if (new_ul_x > 0) 7034 copy_w = context->width - new_ul_x; 7035 else 7036 copy_w = context->width; 7037 if (new_ul_y > 0) 7038 copy_h = context->height - new_ul_y; 7039 else 7040 copy_h = context->height; 7041 /* FIXME: verify this applies to write page, not display page */ 7042 copy_overlapping_area(context->destination_graphic, 7043 new_ul_x, new_ul_y, 7044 0, 0, 7045 (unsigned) copy_w, (unsigned) copy_h, 7046 context->background); 7047 context->destination_graphic->dirty = 1; 7048 context->force_refresh = 1; 7049 } 7050 break; 7051 case 't': 7052 /* FIXME: verify this is in pixels, not user coordinates */ 7053 if (!load_regis_pixel_extent(fragment_to_tempstr(&item), 7054 0, 0, 7055 &context->current_text_controls->character_inc_x, 7056 &context->current_text_controls->character_inc_y)) { 7057 TRACE(("DATA_ERROR: unable to parse extent in '%c' command: \"%s\"\n", 7058 state->command, fragment_to_tempstr(&item))); 7059 break; 7060 } 7061 TRACE(("setting character spacing to %d,%d\n", 7062 context->current_text_controls->character_inc_x, 7063 context->current_text_controls->character_inc_y)); 7064 break; 7065 case 'v': 7066 { 7067 int orig_x, orig_y; 7068 7069 orig_x = context->graphics_output_cursor_x; 7070 orig_y = context->graphics_output_cursor_y; 7071 if (!load_regis_coord_extent(context, 7072 fragment_to_tempstr(&item), 7073 orig_x, orig_y, 7074 &context->graphics_output_cursor_x, 7075 &context->graphics_output_cursor_y)) { 7076 TRACE(("DATA_ERROR: unable to parse extent in '%c' command: \"%s\"\n", 7077 state->command, fragment_to_tempstr(&item))); 7078 break; 7079 } 7080 TRACE(("drawing line to location %d,%d\n", 7081 context->graphics_output_cursor_x, 7082 context->graphics_output_cursor_y)); 7083 draw_patterned_line(context, 7084 orig_x, orig_y, 7085 context->graphics_output_cursor_x, 7086 context->graphics_output_cursor_y); 7087 } 7088 break; 7089 default: 7090 TRACE(("DATA_ERROR: unexpected extent in \"%c\" command: \"%s\"\n", 7091 state->command, fragment_to_tempstr(&item))); 7092 break; 7093 } 7094 return 1; 7095 } 7096 7097 if (state->command != 'l' && extract_regis_pixelvector(input, &item)) { 7098 TRACE(("found pixel vector \"%s\"\n", fragment_to_tempstr(&item))); 7099 switch (state->command) { 7100 case 'c': 7101 /* FIXME: parse, handle */ 7102 TRACE(("pixelvector in curve command FIXME\n")); 7103 break; 7104 /* FIXME: not sure if 'f' supports PVs */ 7105 case 'p': 7106 /* FIXME: error checking */ 7107 if (!load_regis_coord_pixelvector(context, 7108 fragment_to_tempstr(&item), 7109 context->graphics_output_cursor_x, 7110 context->graphics_output_cursor_y, 7111 &context->graphics_output_cursor_x, 7112 &context->graphics_output_cursor_y)) { 7113 TRACE(("DATA_ERROR: unable to parse pixel vector in '%c' command: \"%s\"\n", 7114 state->command, fragment_to_tempstr(&item))); 7115 break; 7116 } 7117 TRACE(("moving pen to location %d,%d\n", 7118 context->graphics_output_cursor_x, 7119 context->graphics_output_cursor_y)); 7120 break; 7121 case 's': 7122 TRACE(("pixelvector scroll argument to screen command: \"%s\"\n", 7123 fragment_to_tempstr(&item))); 7124 { 7125 int old_ul_x, old_ul_y; 7126 int new_ul_x, new_ul_y; 7127 int copy_w, copy_h; 7128 7129 old_ul_x = 0; 7130 old_ul_y = 0; 7131 TRACE(("current upper-left coordinate in pixel coordinates: %d,%d\n", 7132 old_ul_x, old_ul_y)); 7133 /* FIXME: verify this is in user coordinates, not pixels */ 7134 /* FIXME: verify that multiple PV digits result in a single 7135 * copy (this changes the contents of any exposed edge) 7136 */ 7137 if (!load_regis_coord_pixelvector(context, 7138 fragment_to_tempstr(&item), 7139 old_ul_x, old_ul_y, 7140 &new_ul_x, &new_ul_y)) { 7141 TRACE(("DATA_ERROR: unable to parse pixel vector in '%c' command: \"%s\"\n", 7142 state->command, fragment_to_tempstr(&item))); 7143 break; 7144 } 7145 TRACE(("scrolling image to updated upper-left coordinate in pixel coordinates: %d,%d\n", 7146 new_ul_x, new_ul_y)); 7147 7148 /* FIXME: does any write mode affect revealed background? */ 7149 if (new_ul_x > 0) 7150 copy_w = context->width - new_ul_x; 7151 else 7152 copy_w = context->width; 7153 if (new_ul_y > 0) 7154 copy_h = context->height - new_ul_y; 7155 else 7156 copy_h = context->height; 7157 /* FIXME: verify this applies to write page, not display page */ 7158 copy_overlapping_area(context->destination_graphic, 7159 new_ul_x, new_ul_y, 7160 0, 0, 7161 (unsigned) copy_w, (unsigned) copy_h, 7162 context->background); 7163 context->destination_graphic->dirty = 1; 7164 context->force_refresh = 1; 7165 } 7166 break; 7167 case 't': 7168 { 7169 int dx, dy; 7170 7171 /* FIXME: verify this does not use user coordinates */ 7172 if (!load_regis_pixel_pixelvector(fragment_to_tempstr(&item), 7173 1, 0, 0, &dx, &dy)) { 7174 TRACE(("DATA_ERROR: unable to parse pixel vector in '%c' command: \"%s\"\n", 7175 state->command, fragment_to_tempstr(&item))); 7176 break; 7177 } 7178 7179 dx *= (int) (context->current_text_controls->character_display_w 7180 >> 1U); 7181 dy *= (int) (context->current_text_controls->character_display_h 7182 >> 1U); 7183 TRACE(("adding character offset %d,%d\n (ds=%dx%d)", dx, dy, 7184 context->current_text_controls->character_display_w, 7185 context->current_text_controls->character_display_h)); 7186 move_text(context, dx, dy); 7187 } 7188 break; 7189 case 'v': 7190 { 7191 char const *const pixelvector = fragment_to_tempstr(&item); 7192 unsigned offset; 7193 7194 for (offset = 0U; pixelvector[offset] != '\0';) { 7195 int orig_x = context->graphics_output_cursor_x; 7196 int orig_y = context->graphics_output_cursor_y; 7197 if (!load_regis_coord_pixelvector_step(context, pixelvector, 7198 &offset, 7199 orig_x, orig_y, 7200 &context->graphics_output_cursor_x, 7201 &context->graphics_output_cursor_y)) { 7202 TRACE(("DATA_ERROR: unable to parse pixel vector in '%c' command: \"%s\"\n", 7203 state->command, fragment_to_tempstr(&item))); 7204 break; 7205 } 7206 TRACE(("drawing line to location %d,%d\n", 7207 context->graphics_output_cursor_x, 7208 context->graphics_output_cursor_y)); 7209 draw_patterned_line(context, orig_x, orig_y, 7210 context->graphics_output_cursor_x, 7211 context->graphics_output_cursor_y); 7212 } 7213 } 7214 break; 7215 default: 7216 TRACE(("DATA_ERROR: unexpected pixel vector in \"%c\" command: \"%s\"\n", 7217 state->command, fragment_to_tempstr(&item))); 7218 break; 7219 } 7220 return 1; 7221 } 7222 7223 if (extract_regis_string(input, state->temp, state->templen)) { 7224 switch (state->command) { 7225 case 'l': 7226 /* FIXME: confirm that extra characters are ignored */ 7227 TRACE(("found character to load: \"%s\"\n", state->temp)); 7228 state->load_glyph = (unsigned) (Char) state->temp[0]; 7229 state->load_row = 0U; 7230 break; 7231 case 't': 7232 TRACE(("found string to draw: \"%s\"\n", state->temp)); 7233 draw_text(context, state->temp); 7234 break; 7235 case '_': 7236 TRACE(("found comment: \"%s\"\n", state->temp)); 7237 break; 7238 default: 7239 TRACE(("DATA_ERROR: unexpected string argument to \"%c\" command: \"%s\"\n", 7240 state->command, state->temp)); 7241 break; 7242 } 7243 return 1; 7244 } 7245 7246 /* hex values */ 7247 if (state->command == 'l') { 7248 unsigned digit; 7249 7250 for (digit = 0U; digit < (state->load_w + 3U) >> 2U; digit++) { 7251 char ch = peek_fragment(input); 7252 7253 if (!isxdigit(CharOf(ch))) { 7254 if (ch != ',' && ch != ';' && 7255 ch != ' ' && ch != '\r' && 7256 ch != '\n') { 7257 TRACE(("found end of hexadecimal string witch '%c' on digit %u\n", 7258 ch, digit)); 7259 /* FIXME: need to unput the digits up to this point */ 7260 /* 7261 * Report success since we ate some characters, 7262 * and the new char needs to be compared with commands 7263 * and other top-level things. 7264 */ 7265 if (digit != 0U) 7266 return 1; 7267 return 0; 7268 } 7269 pop_fragment(input); 7270 break; 7271 } 7272 7273 state->temp[digit] = ch; 7274 pop_fragment(input); 7275 } 7276 state->temp[digit] = '\0'; 7277 7278 if (strlen(state->temp) > 0) { 7279 unsigned long val; 7280 unsigned glyph_size; 7281 7282 val = strtoul(state->temp, NULL, 16); 7283 TRACE(("found row %u for glyph %u: \"%s\" value %02lx (%lu)\n", 7284 state->load_row, state->load_glyph, state->temp, val, val)); 7285 7286 if (state->load_row >= state->load_h) { 7287 TRACE(("DATA_ERROR: ignoring extra row for glyph %u\n", 7288 state->load_glyph)); 7289 return 0; 7290 } 7291 7292 if (state->load_index == MAX_REGIS_ALPHABETS) { 7293 state->load_index = find_free_alphabet_index(context, 7294 state->load_alphabet, 7295 state->load_w, 7296 state->load_h); 7297 TRACE(("current alphabet is %u and size is %ux%u; assigning alphabet index %u\n", 7298 state->load_alphabet, state->load_w, 7299 state->load_h, state->load_index)); 7300 } 7301 7302 glyph_size = 7303 GLYPH_WIDTH_BYTES(context->alphabets[state->load_index].pixw) 7304 * context->alphabets[state->load_index].pixh; 7305 if (context->alphabets[state->load_index].bytes == NULL) { 7306 if (!(context->alphabets[state->load_index].bytes = 7307 calloc((size_t) (MAX_GLYPHS * glyph_size), sizeof(Char)))) { 7308 TRACE(("ERROR: unable to allocate %u bytes for glyph storage\n", 7309 MAX_GLYPHS * glyph_size)); 7310 return 0; 7311 } 7312 } { 7313 Char *glyph; 7314 unsigned bytew; 7315 unsigned byte; 7316 unsigned unused_bits; 7317 7318 glyph = &context->alphabets[state->load_index] 7319 .bytes[state->load_glyph * glyph_size]; 7320 bytew = GLYPH_WIDTH_BYTES(context->alphabets[state->load_index] 7321 .pixw); 7322 unused_bits = 8U - (context->alphabets[state->load_index].pixw 7323 & 3U); 7324 if (unused_bits == 8U) { 7325 unused_bits = 0U; 7326 } 7327 for (byte = 0U; byte < bytew; byte++) { 7328 glyph[state->load_row * bytew + byte] = 7329 (Char) (((val << unused_bits) >> 7330 ((bytew - (byte + 1U)) << 3U)) 7331 & 255U); 7332#ifdef DEBUG_LOAD 7333 TRACE(("bytew=%u val=%lx byte=%u output=%x\n", 7334 bytew, val, 7335 byte, 7336 (unsigned) glyph[state->load_row * bytew + byte])); 7337#endif 7338 } 7339 7340 state->load_row++; 7341 context->alphabets[state->load_index].use_font = 0; 7342 context->alphabets[state->load_index] 7343 .loaded[state->load_glyph] = 1; 7344#ifdef DEBUG_LOAD 7345 TRACE(("marking alphabet %u at index %u glyph %u as loaded\n", 7346 state->load_alphabet, state->load_index, 7347 state->load_glyph)); 7348#endif 7349 return 1; 7350 } 7351 } 7352 } 7353 7354 /* special symbols */ 7355 if (state->command == '@') { 7356 char ch = peek_fragment(input); 7357 TRACE(("inspecting macrograph character \"%c\"\n", ch)); 7358 switch (ch) { 7359 case '.': 7360 pop_fragment(input); 7361 TRACE(("clearing all macrographs\n")); 7362 /* FIXME: implement when macrographs are supported. */ 7363 return 1; 7364 case ':': 7365 pop_fragment(input); 7366 TRACE(("defining macrograph\n")); 7367 /* FIXME: what about whitespace before the name? */ 7368 if (fragment_consumed(input)) { 7369 TRACE(("DATA_ERROR: macrograph definition without name, ignoring\n")); 7370 return 1; 7371 } { 7372#define MAX_MACROGRAPH_LEN 1024 7373 char temp[MAX_MACROGRAPH_LEN]; 7374 char name; 7375 char prev = '\0'; 7376 int len = 0; 7377 7378 name = pop_fragment(input); 7379 if (islower(CharOf(name))) 7380 name = (char) toupper(CharOf(name)); 7381 TRACE(("defining macrograph for \"%c\"\n", name)); 7382 if (name < 'A' || name > 'Z') { 7383 TRACE(("DATA_ERROR: invalid macrograph name\n")); 7384 /* FIXME: what should happen? */ 7385 return 1; 7386 } 7387 for (;;) { 7388 char next = peek_fragment(input); 7389 if (prev == '@' && next == ';') { 7390 /* FIXME: parse, handle :<name><definition>; */ 7391 pop_fragment(input); 7392 len--; 7393 break; 7394 } else if (next == '\0') { 7395 TRACE(("DATA_ERROR: macrograph definition ends before semicolon\n")); 7396 /* FIXME: what should happen? */ 7397 return 1; 7398 } 7399 pop_fragment(input); 7400 if (len < MAX_MACROGRAPH_LEN) { 7401 temp[len++] = next; 7402 } 7403 prev = next; 7404 } 7405 if (len == MAX_MACROGRAPH_LEN) { 7406 TRACE(("DATA_ERROR: macrograph definition too long\n")); 7407 /* FIXME: what should happen? */ 7408 return 1; 7409 } 7410 temp[len] = '\0'; 7411 7412 (void) temp; /* variable needed only if tracing */ 7413 /* FIXME: implement when macrographs are supported. */ 7414 TRACE(("ERROR: would have saved macrograph: \"%s\"\n", temp)); 7415 } 7416 return 1; 7417 case ';': 7418 pop_fragment(input); 7419 TRACE(("DATA_ERROR: found extraneous terminator for macrograph definition\n")); 7420 return 1; 7421 default: 7422 pop_fragment(input); 7423 TRACE(("DATA_ERROR: unknown macrograph command '%c'\n", ch)); 7424 return 1; 7425 } 7426 } 7427 7428 return 0; 7429} 7430 7431static int 7432parse_regis_toplevel(RegisParseState *state, RegisGraphicsContext *context) 7433{ 7434 RegisDataFragment parenthesized; 7435 char ch; 7436#if 0 7437 TRACE(("reference line: shading=%d ref=%u loc=%d\n", 7438 context->temporary_write_controls.shading_enabled, 7439 context->temporary_write_controls.shading_reference_dim, 7440 context->temporary_write_controls.shading_reference)); 7441#endif 7442 7443#ifdef DEBUG_PARSING 7444 TRACE(("parsing top level: char %u of %u (next char '%c')\n", 7445 state->input.pos, 7446 fragment_length(&state->input), 7447 peek_fragment(&state->input))); 7448#endif 7449 if (skip_regis_whitespace(&state->input)) 7450 return 0; 7451 if (expand_macrographs(&state->input, context)) 7452 return 0; 7453 7454 /* FIXME: the semicolon terminates the current command even if inside of an optionset or extent */ 7455 if (peek_fragment(&state->input) == ';') { 7456 pop_fragment(&state->input); 7457 TRACE(("ending '%c' command\n", state->command)); 7458 state->command = '_'; 7459 state->option = '_'; 7460 return 1; 7461 } 7462 /* Load statements contain hex values which may look like commands. */ 7463 ch = peek_fragment(&state->input); 7464 if (state->command != 'l' || !isxdigit(CharOf(ch))) { 7465#ifdef DEBUG_PARSING 7466 TRACE(("checking for top level command...\n")); 7467#endif 7468 if (parse_regis_command(state)) { 7469 /* FIXME: verify that these are the things reset on a new command */ 7470 TRACE(("resetting temporary write controls and pattern state before handling command\n")); 7471 copy_regis_write_controls(&context->persistent_write_controls, 7472 &context->temporary_write_controls); 7473 context->pattern_count = 0U; 7474 context->pattern_bit = 1U; 7475 7476 /* FIXME: what happens if temporary text controls aren't closed? */ 7477 /* FIXME: what if temporary text controls are nested? */ 7478 context->current_text_controls = &context->persistent_text_controls; 7479 return 1; 7480 } 7481 } 7482#ifdef DEBUG_PARSING 7483 TRACE(("checking for top level parentheses...\n")); 7484#endif 7485 if (extract_regis_parenthesized_data(&state->input, &parenthesized)) { 7486 RegisDataFragment keep; 7487 7488 if (state->command == 'f') { /* Fill */ 7489 TRACE(("found commands in fill mode \"%s\"\n", 7490 fragment_to_tempstr(&parenthesized))); 7491 copy_fragment(&keep, &state->input); 7492 copy_fragment(&state->input, &parenthesized); 7493 state->command = '_'; 7494 state->option = '_'; 7495 context->fill_mode = 1; 7496 context->fill_point_count = 0U; 7497 while (!fragment_consumed(&state->input)) 7498 parse_regis_toplevel(state, context); 7499 if (context->temporary_write_controls.shading_character != '\0') { 7500 draw_shaded_polygon(context); 7501 } else { 7502 draw_filled_polygon(context); 7503 } 7504 context->fill_point_count = 0U; 7505 context->fill_mode = 0; 7506 state->command = 'f'; 7507 copy_fragment(&state->input, &keep); 7508 return 1; 7509 } else { 7510 TRACE(("parsing optionset \"%s\"\n", 7511 fragment_to_tempstr(&parenthesized))); 7512 copy_fragment(&keep, &state->input); 7513 copy_fragment(&state->input, &parenthesized); 7514 state->option = '_'; 7515 TRACE(("parsing at optionset level (char %u of %u)\n", 7516 state->input.pos, 7517 fragment_length(&state->input))); 7518 for (;;) { 7519 if (fragment_consumed(&state->input)) 7520 break; 7521 TRACE(("looking at optionset character %u: \"%c\"\n", 7522 state->input.pos, peek_fragment(&state->input))); 7523 if (skip_regis_whitespace(&state->input)) 7524 continue; 7525 if (parse_regis_option(state, context)) 7526 continue; 7527 if (parse_regis_items(state, context)) 7528 continue; 7529 if (fragment_consumed(&state->input)) 7530 break; 7531 { 7532 char skip; 7533 7534 skip = pop_fragment(&state->input); 7535 (void) skip; /* variable needed only if tracing */ 7536 TRACE(("DATA_ERROR: skipping unexpected character in optionset: \"%c\"\n", 7537 skip)); 7538 } 7539 /* FIXME: suboptions */ 7540 } 7541 state->option = '_'; 7542 copy_fragment(&state->input, &keep); 7543 return 1; 7544 } 7545 } 7546 if (state->command == 'f') { /* Fill */ 7547 RegisDataFragment optionarg; 7548 if (extract_regis_option(&state->input, &state->option, &optionarg)) { 7549 skip_regis_whitespace(&optionarg); 7550 7551 TRACE(("found temporary write options \"%s\"\n", 7552 fragment_to_tempstr(&optionarg))); 7553 if (!load_regis_write_control_set(state, context, 7554 context->graphics_output_cursor_x, 7555 context->graphics_output_cursor_y, 7556 &optionarg, 7557 &context->temporary_write_controls)) { 7558 TRACE(("DATA_ERROR: invalid temporary write options \"%s\"\n", 7559 fragment_to_tempstr(&optionarg))); 7560 } 7561 return 1; 7562 } 7563 TRACE(("checking for top level items (though none should be present)...\n")); 7564 if (parse_regis_items(state, context)) 7565 return 1; 7566 } else { 7567#ifdef DEBUG_PARSING 7568 TRACE(("checking for top level items...\n")); 7569#endif 7570 if (parse_regis_items(state, context)) 7571 return 1; 7572 } 7573 7574 if (!fragment_consumed(&state->input)) { 7575 char skip; 7576 7577 skip = pop_fragment(&state->input); 7578 (void) skip; /* variable needed only if tracing */ 7579 TRACE(("DATA_ERROR: skipping unexpected character at top level: \"%c\"\n", ch)); 7580 } 7581 return 0; 7582} 7583 7584void 7585parse_regis(XtermWidget xw, ANSI *params, char const *string) 7586{ 7587 TScreen *screen = TScreenOf(xw); 7588 RegisGraphicsContext *const context = &persistent_context; 7589 RegisParseState *const state = &persistent_state; 7590 struct timeval prev_tv; 7591 struct timeval curr_tv; 7592 unsigned iterations; 7593 int Pmode; 7594 7595 assert(params); 7596 assert(string); 7597 7598 if (params->a_nparam > 0) 7599 Pmode = params->a_param[0]; 7600 else 7601 Pmode = 0; 7602 7603 TRACE(("ReGIS vector graphics mode, param_count=%d mode=%d\n", 7604 params->a_nparam, Pmode)); 7605 7606 init_fragment(&state->input, string); 7607 state->templen = (unsigned) strlen(string) + 1U; 7608 if (!(state->temp = malloc((size_t) state->templen))) { 7609 TRACE(("Unable to allocate temporary buffer of size %u\n", 7610 state->templen)); 7611 return; 7612 } 7613 7614 context->current_widget = xw; 7615 7616 /* Update the screen scrolling and do a refresh. 7617 * The refresh may not cover the whole graphic. 7618 */ 7619 if (screen->scroll_amt) 7620 FlushScroll(xw); 7621 7622 /* Only reset on the first ReGIS image unless it is being requested. */ 7623 if (context->width == 0 || context->height == 0 || 7624 Pmode == 1 || Pmode == 3) { 7625 init_regis_parse_state(state); 7626 init_regis_graphics_context(GraphicsTermId(screen), 7627 screen->graphics_regis_def_wide, 7628 screen->graphics_regis_def_high, 7629 get_color_register_count(screen), 7630 screen->graphics_regis_default_font, 7631 context); 7632 } 7633 7634 map_regis_graphics_pages(xw, context); 7635 7636 X_GETTIMEOFDAY(&prev_tv); 7637 iterations = 0U; 7638 refresh_modified_displayed_graphics(xw); 7639 7640 for (;;) { 7641 if (skip_regis_whitespace(&state->input)) 7642 continue; 7643 if (parse_regis_toplevel(state, context)) { 7644 int need_refresh = 0; 7645 7646 /* FIXME: Move refresh logic out of the top level so that long 7647 * sequences of filled drawing commands can be refreshed before the 7648 * end of the fill command. 7649 */ 7650 iterations++; 7651 memset(&curr_tv, 0, sizeof(curr_tv)); 7652 if (context->force_refresh) { 7653 X_GETTIMEOFDAY(&curr_tv); 7654 need_refresh = 1; 7655 } else if (iterations > MIN_ITERATIONS_BEFORE_REFRESH) { 7656 X_GETTIMEOFDAY(&curr_tv); 7657 if ((Time) curr_tv.tv_sec > (Time) prev_tv.tv_sec + 1UL) { 7658 need_refresh = 1; 7659 } else { 7660#define DiffTime(TV) (TV.tv_sec * 1000L + TV.tv_usec / 1000L) 7661 long diff = (long) (DiffTime(curr_tv) - DiffTime(prev_tv)); 7662 if (diff > MIN_MS_BEFORE_REFRESH) 7663 need_refresh = 1; 7664 } 7665 } 7666 7667 if (need_refresh) { 7668 TRACE(("refreshing after %u iterations and %ldms\n", 7669 iterations, 7670 (long) (DiffTime(curr_tv) - DiffTime(prev_tv)))); 7671 context->force_refresh = 0; 7672 prev_tv = curr_tv; 7673 iterations = 0U; 7674 refresh_modified_displayed_graphics(xw); 7675 xtermFlushDbe(xw); 7676 } 7677 7678 continue; 7679 } 7680 7681 if (fragment_consumed(&state->input)) 7682 break; 7683 } 7684 7685 free(state->temp); 7686 7687 refresh_modified_displayed_graphics(xw); 7688 TRACE(("DONE! Successfully parsed ReGIS data.\n")); 7689} 7690