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