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