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