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