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