graphics_regis.c revision 894e0ac8
1/* $XTermId: graphics_regis.c,v 1.26 2014/05/27 00:18:05 tom Exp $ */ 2 3/* 4 * Copyright 2014 by Ross Combs 5 * 6 * All Rights Reserved 7 * 8 * Permission is hereby granted, free of charge, to any person obtaining a 9 * copy of this software and associated documentation files (the 10 * "Software"), to deal in the Software without restriction, including 11 * without limitation the rights to use, copy, modify, merge, publish, 12 * distribute, sublicense, and/or sell copies of the Software, and to 13 * permit persons to whom the Software is furnished to do so, subject to 14 * the following conditions: 15 * 16 * The above copyright notice and this permission notice shall be included 17 * in all copies or substantial portions of the Software. 18 * 19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 20 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 22 * IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY 23 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 24 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 25 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 26 * 27 * Except as contained in this notice, the name(s) of the above copyright 28 * holders shall not be used in advertising or otherwise to promote the 29 * sale, use or other dealings in this Software without prior written 30 * authorization. 31 */ 32 33#include <xterm.h> 34 35#include <stdio.h> 36#include <ctype.h> 37#include <math.h> 38#include <stdlib.h> 39 40#include <data.h> 41#include <VTparse.h> 42#include <ptyx.h> 43 44#include <assert.h> 45#include <graphics.h> 46#include <graphics_regis.h> 47 48/* get rid of shadowing warnings (we will not draw Bessel functions) */ 49#define y1 my_y1 50#define y0 my_y0 51 52#undef DEBUG_BEZIER 53#undef DEBUG_SPLINE_SEGMENTS 54#undef DEBUG_SPLINE_POINTS 55#undef DEBUG_SPLINE_WITH_ROTATION 56#undef DEBUG_SPLINE_WITH_OVERDRAW 57#undef DEBUG_ARC_CENTER 58#undef DEBUG_ARC_START 59#undef DEBUG_ARC_END 60 61#define ITERATIONS_BEFORE_REFRESH 100U 62/* *INDENT-OFF* */ 63typedef struct RegisWriteControls { 64 unsigned pv_multiplier; 65 unsigned pattern; 66 unsigned pattern_multiplier; 67 unsigned invert_pattern; 68 unsigned plane_mask; 69 unsigned write_style; 70 RegisterNum foreground; 71 unsigned shading_enabled; 72 char shading_character; 73 int shading_reference; 74 unsigned shading_reference_dim; 75} RegisWriteControls; 76 77typedef struct RegisDataFragment { 78 char const *start; 79 unsigned pos; 80 unsigned len; 81} RegisDataFragment; 82 83typedef enum RegisParseLevel { 84 INPUT, 85 OPTIONSET 86} RegisParseLevel; 87/* *INDENT-ON* */ 88 89#define CURVE_POSITION_ARC_EDGE 0U 90#define CURVE_POSITION_ARC_CENTER 1U 91#define CURVE_POSITION_OPEN_CURVE 2U 92#define CURVE_POSITION_CLOSED_CURVE 3U 93 94#define MAX_INPUT_CURVE_POINTS 16U 95#define MAX_CURVE_POINTS (MAX_INPUT_CURVE_POINTS + 4U) 96 97typedef struct RegisParseState { 98 RegisDataFragment input; 99 RegisDataFragment optionset; 100 char *temp; 101 unsigned templen; 102 RegisParseLevel level; 103 char command; 104 char option; 105 /* curve options */ 106 int curve_mode; 107 int arclen; 108 int x_points[MAX_CURVE_POINTS]; 109 int y_points[MAX_CURVE_POINTS]; 110 unsigned num_points; 111} RegisParseState; 112 113typedef struct RegisGraphicsContext { 114 Graphic *graphic; 115 int terminal_id; 116 unsigned all_planes; 117 RegisterNum background; 118 RegisWriteControls persistent_write_controls; 119 RegisWriteControls temporary_write_controls; 120 int graphics_output_cursor_x; 121 int graphics_output_cursor_y; 122 unsigned pattern_count; 123 unsigned pattern_bit; 124} RegisGraphicsContext; 125 126#define MAX_PATTERN_BITS 8U 127 128#define WRITE_STYLE_OVERLAY 1U 129#define WRITE_STYLE_REPLACE 2U 130#define WRITE_STYLE_COMPLEMENT 3U 131#define WRITE_STYLE_ERASE 4U 132 133#define WRITE_SHADING_REF_Y 0U 134#define WRITE_SHADING_REF_X 1U 135 136#define ROT_LEFT_N(V, N) ( (((V) << ((N) & 3U )) & 255U) | ((V) >> (8U - ((N) & 3U))) ) 137#define ROT_LEFT(V) ( (((V) << 1U) & 255U) | ((V) >> 7U) ) 138 139static int 140ifloor(double d) 141{ 142 double dl = floor(d); 143 return (int) dl; 144} 145 146static int 147isqrt(double d) 148{ 149 double dl = sqrt(d); 150 return (int) dl; 151} 152 153static void 154draw_regis_pixel(RegisGraphicsContext *context, int x, int y) 155{ 156 unsigned color = 0; 157 158 switch (context->temporary_write_controls.write_style) { 159 case WRITE_STYLE_OVERLAY: 160 /* update pixels with foreground when pattern is 1, unchanged when pattern is 0 */ 161 if (!(context->temporary_write_controls.pattern & context->pattern_bit)) { 162 return; 163 } 164 165 if (context->temporary_write_controls.invert_pattern) { 166 color = context->background; 167 } else { 168 color = context->temporary_write_controls.foreground; 169 } 170 break; 171 172 case WRITE_STYLE_REPLACE: 173 /* update pixels with foreground when pattern is 1, background when pattern is 0 */ 174 { 175 unsigned fg, bg; 176 177 if (context->temporary_write_controls.invert_pattern) { 178 fg = context->background; 179 bg = context->temporary_write_controls.foreground; 180 } else { 181 fg = context->temporary_write_controls.foreground; 182 bg = context->background; 183 } 184 color = ((context->temporary_write_controls.pattern & 185 context->pattern_bit) 186 ? fg 187 : bg); 188 } 189 break; 190 191 case WRITE_STYLE_COMPLEMENT: 192 /* update pixels with background when pattern is 1, unchanged when pattern is 0 */ 193 color = read_pixel(context->graphic, x, y); 194 if (color == COLOR_HOLE) 195 color = context->background; 196 color = color ^ context->all_planes; 197 break; 198 199 case WRITE_STYLE_ERASE: 200 /* update pixels with foreground */ 201 if (context->temporary_write_controls.invert_pattern) { 202 color = context->temporary_write_controls.foreground; 203 } else { 204 color = context->background; 205 } 206 break; 207 } 208 209 draw_solid_pixel(context->graphic, x, y, color); 210} 211 212static void 213fill_to_pixel(RegisGraphicsContext *context, int x, int y) 214{ 215 unsigned dim = context->temporary_write_controls.shading_reference_dim; 216 int ref = context->temporary_write_controls.shading_reference; 217 218 if (dim == WRITE_SHADING_REF_X) { 219 int delta = x > ref ? 1 : -1; 220 int curr_x; 221 222 context->pattern_bit = 1U << (((unsigned) y) & 7U); 223 for (curr_x = ref; curr_x != x + delta; curr_x += delta) { 224 draw_regis_pixel(context, curr_x, y); 225 } 226 } else { 227 int delta = y > ref ? 1 : -1; 228 int curr_y; 229 230 for (curr_y = ref; curr_y != y + delta; curr_y += delta) { 231 context->pattern_bit = 1U << (((unsigned) curr_y) & 7U); 232 draw_regis_pixel(context, x, curr_y); 233 } 234 } 235} 236 237static void 238draw_patterned_pixel(RegisGraphicsContext *context, int x, int y) 239{ 240 if (context->temporary_write_controls.shading_enabled) { 241 if (context->temporary_write_controls.shading_character != '\0') { 242 /* FIXME: handle character fills */ 243 TRACE(("pixel shaded with character\n")); 244 } else { 245 fill_to_pixel(context, x, y); 246 } 247 } else { 248 if (context->pattern_count >= context->temporary_write_controls.pattern_multiplier) { 249 context->pattern_count = 0U; 250 context->pattern_bit = ROT_LEFT(context->pattern_bit); 251 } 252 context->pattern_count++; 253 254 draw_regis_pixel(context, x, y); 255 } 256} 257 258static void 259draw_patterned_line(RegisGraphicsContext *context, int x1, int y1, int x2, int y2) 260{ 261 int x, y; 262 int dx, dy; 263 int dir, diff; 264 265 dx = abs(x1 - x2); 266 dy = abs(y1 - y2); 267 268 if (dx > dy) { 269 if (x1 > x2) { 270 int tmp; 271 EXCHANGE(x1, x2, tmp); 272 EXCHANGE(y1, y2, tmp); 273 } 274 if (y1 < y2) 275 dir = 1; 276 else if (y1 > y2) 277 dir = -1; 278 else 279 dir = 0; 280 281 diff = 0; 282 y = y1; 283 for (x = x1; x <= x2; x++) { 284 if (diff >= dx) { 285 diff -= dx; 286 y += dir; 287 } 288 diff += dy; 289 draw_patterned_pixel(context, x, y); 290 } 291 } else { 292 if (y1 > y2) { 293 int tmp; 294 EXCHANGE(y1, y2, tmp); 295 EXCHANGE(x1, x2, tmp); 296 } 297 if (x1 < x2) 298 dir = 1; 299 else if (x1 > x2) 300 dir = -1; 301 else 302 dir = 0; 303 304 diff = 0; 305 x = x1; 306 for (y = y1; y <= y2; y++) { 307 if (diff >= dy) { 308 diff -= dy; 309 x += dir; 310 } 311 diff += dx; 312 draw_patterned_pixel(context, x, y); 313 } 314 } 315} 316 317typedef struct { 318 int dxx; 319 int dxy; 320 int dyx; 321 int dyy; 322} quadmap_coords; 323 324static void 325draw_patterned_arc(RegisGraphicsContext *context, 326 int cx, int cy, 327 int ex, int ey, 328 int a_start, int a_length, 329 int *ex_final, int *ey_final) 330{ 331 const double third = hypot((double) (cx - ex), (double) (cy - ey)); 332 const int radius = (int) third; 333 const int ra = radius; 334 const int rb = radius; 335 const quadmap_coords neg_quadmap[4] = 336 { 337 {-1, 0, 0, +1}, 338 {0, -1, -1, 0}, 339 {+1, 0, 0, -1}, 340 {0, +1, +1, 0}, 341 }; 342 const quadmap_coords pos_quadmap[4] = 343 { 344 {-1, 0, 0, -1}, 345 {0, -1, +1, 0}, 346 {+1, 0, 0, +1}, 347 {0, +1, -1, 0}, 348 }; 349 const quadmap_coords *quadmap; 350 int total_points; 351 int points_start, points_stop; 352 int points; 353 unsigned iterations; 354 int quad; 355 long rx, ry; 356 long dx, dy; 357 int x, y; 358 long e2; 359 long error; 360 361 TRACE(("a_length=%d a_start=%d\n", a_length, a_start)); 362 if (a_length == 0) 363 return; 364 if (a_length > 0) { 365 quadmap = pos_quadmap; 366 } else { 367 quadmap = neg_quadmap; 368 if (a_start != 0) 369 a_start = 360 - a_start; 370 } 371 372 rx = -ra; 373 ry = 0; 374 e2 = rb; 375 dx = (2 * rx + 1) * e2 * e2; 376 dy = rx * rx; 377 error = dx + dy; 378 total_points = 0; 379 do { 380 total_points += 4; 381 e2 = 2 * error; 382 if (e2 >= dx) { 383 rx++; 384 dx += 2 * rb * rb; 385 error += dx; 386 } 387 if (e2 <= dy) { 388 ry++; 389 dy += 2 * ra * ra; 390 error += dy; 391 } 392 } 393 while (rx <= 0); 394 points_start = (total_points * a_start) / 360; 395 points_stop = (total_points * a_start + 396 total_points * abs(a_length) + 359) / 360; 397 TRACE(("drawing arc with %d points from %d angle for %d degrees (from point %d to %d out of %d)\n", 398 total_points, a_start, a_length, points_start, points_stop, total_points)); 399 400 points = 0; 401 for (iterations = 0U; iterations < 8U; iterations++) { 402 quad = iterations & 0x3; 403 404 rx = -ra; 405 ry = 0; 406 e2 = rb; 407 dx = (2 * rx + 1) * e2 * e2; 408 dy = rx * rx; 409 error = dx + dy; 410 do { 411 if (points >= points_start && points <= points_stop) { 412 x = (int) (cx + 413 quadmap[quad].dxx * rx + 414 quadmap[quad].dxy * ry); 415 y = (int) (cy + 416 quadmap[quad].dyx * rx + 417 quadmap[quad].dyy * ry); 418 draw_patterned_pixel(context, x, y); 419 if (ex_final) 420 *ex_final = x; 421 if (ey_final) 422 *ey_final = y; 423 } 424 points++; 425 426 e2 = 2 * error; 427 if (e2 >= dx) { 428 rx++; 429 dx += 2 * rb * rb; 430 error += dx; 431 } 432 if (e2 <= dy) { 433 ry++; 434 dy += 2 * ra * ra; 435 error += dy; 436 } 437 } 438 while (rx <= 0); 439 } 440} 441 442/* 443 * The plot* functions are based on optimized rasterization primitves written by Zingl Alois. 444 * See http://members.chello.at/easyfilter/bresenham.html 445 */ 446 447/* 448 * FIXME: 449 * This is a terrible temporary hack. The plot functions below can be adapted 450 * to work like the other rasterization functions but there's no point in doing 451 * that until we know we don't have to write something completely different. 452 */ 453static RegisGraphicsContext *global_context; 454static void 455setPixel(int x, int y) 456{ 457 draw_patterned_pixel(global_context, x, y); 458} 459 460static void 461plotLine(int x0, int y0, int x1, int y1) 462{ 463 int dx = abs(x1 - x0), sx = x0 < x1 ? 1 : -1; 464 int dy = -abs(y1 - y0), sy = y0 < y1 ? 1 : -1; 465 int err = dx + dy, e2; /* error value e_xy */ 466 467 for (;;) { /* loop */ 468 setPixel(x0, y0); 469 e2 = 2 * err; 470 if (e2 >= dy) { /* e_xy+e_x > 0 */ 471 if (x0 == x1) 472 break; 473 err += dy; 474 x0 += sx; 475 } 476 if (e2 <= dx) { /* e_xy+e_y < 0 */ 477 if (y0 == y1) 478 break; 479 err += dx; 480 y0 += sy; 481 } 482 } 483} 484 485static void 486plotQuadBezierSeg(int x0, int y0, int x1, int y1, int x2, int y2) 487{ /* plot a limited quadratic Bezier segment */ 488 int sx = x2 - x1; 489 int sy = y2 - y1; 490 long xx = (x0 - x1); /* relative values for checks */ 491 long yy = (y0 - y1); 492 long xy; 493 double dx, dy, err; 494 double cur = (double) (xx * sy - yy * sx); /* curvature */ 495 496 assert(xx * sx <= 0 && yy * sy <= 0); /* sign of gradient must not change */ 497 498 if (sx * (long) sx + sy * (long) sy > xx * xx + yy * yy) { /* begin with longer part */ 499 x2 = x0; 500 x0 = sx + x1; 501 y2 = y0; 502 y0 = sy + y1; 503 cur = -cur; /* swap P0 P2 */ 504 } 505 if (cur != 0) { /* no straight line */ 506 xx += sx; 507 xx *= sx = x0 < x2 ? 1 : -1; /* x step direction */ 508 yy += sy; 509 yy *= sy = y0 < y2 ? 1 : -1; /* y step direction */ 510 xy = 2 * xx * yy; 511 xx *= xx; 512 yy *= yy; /* differences 2nd degree */ 513 if (cur * sx * sy < 0) { /* negated curvature? */ 514 xx = -xx; 515 yy = -yy; 516 xy = -xy; 517 cur = -cur; 518 } 519 /* differences 1st degree */ 520 dx = ((4.0 * sy * cur * (x1 - x0)) + (double) xx) - (double) xy; 521 dy = ((4.0 * sx * cur * (y0 - y1)) + (double) yy) - (double) xy; 522 xx += xx; 523 yy += yy; 524 err = dx + dy + (double) xy; /* error 1st step */ 525 do { 526 setPixel(x0, y0); /* plot curve */ 527 if (x0 == x2 && y0 == y2) 528 return; /* last pixel -> curve finished */ 529 y1 = (2 * err) < dx; /* save value for test of y step */ 530 if ((2 * err) > dy) { 531 x0 += sx; 532 dx -= (double) xy; 533 dy += (double) yy; 534 err += dy; 535 } /* x step */ 536 if (y1) { 537 y0 += sy; 538 dy -= (double) xy; 539 dx += (double) xx; 540 err += dx; 541 } /* y step */ 542 } while (dy < 0 && dx > 0); /* gradient negates -> algorithm fails */ 543 } 544 plotLine(x0, y0, x2, y2); /* plot remaining part to end */ 545} 546 547#if 0 548static void 549plotQuadBezier(int x0, int y0, int x1, int y1, int x2, int y2) 550{ /* plot any quadratic Bezier curve */ 551 int x = x0 - x1; 552 int y = y0 - y1; 553 double t = x0 - 2 * x1 + x2; 554 double r; 555 556 if ((long) x * (x2 - x1) > 0) { /* horizontal cut at P4? */ 557 if ((long) y * (y2 - y1) > 0) /* vertical cut at P6 too? */ 558 if (fabs((y0 - 2 * y1 + y2) / t * x) > abs(y)) { /* which first? */ 559 x0 = x2; 560 x2 = x + x1; 561 y0 = y2; 562 y2 = y + y1; /* swap points */ 563 } /* now horizontal cut at P4 comes first */ 564 t = (x0 - x1) / t; 565 r = (1 - t) * ((1 - t) * y0 + 2.0 * t * y1) + t * t * y2; /* By(t=P4) */ 566 t = (x0 * x2 - x1 * x1) * t / (x0 - x1); /* gradient dP4/dx=0 */ 567 x = ifloor(t + 0.5); 568 y = ifloor(r + 0.5); 569 r = (y1 - y0) * (t - x0) / (x1 - x0) + y0; /* intersect P3 | P0 P1 */ 570 plotQuadBezierSeg(x0, y0, x, ifloor(r + 0.5), x, y); 571 r = (y1 - y2) * (t - x2) / (x1 - x2) + y2; /* intersect P4 | P1 P2 */ 572 x0 = x1 = x; 573 y0 = y; 574 y1 = ifloor(r + 0.5); /* P0 = P4, P1 = P8 */ 575 } 576 if ((long) (y0 - y1) * (y2 - y1) > 0) { /* vertical cut at P6? */ 577 t = y0 - 2 * y1 + y2; 578 t = (y0 - y1) / t; 579 r = (1 - t) * ((1 - t) * x0 + 2.0 * t * x1) + t * t * x2; /* Bx(t=P6) */ 580 t = (y0 * y2 - y1 * y1) * t / (y0 - y1); /* gradient dP6/dy=0 */ 581 x = ifloor(r + 0.5); 582 y = ifloor(t + 0.5); 583 r = (x1 - x0) * (t - y0) / (y1 - y0) + x0; /* intersect P6 | P0 P1 */ 584 plotQuadBezierSeg(x0, y0, ifloor(r + 0.5), y, x, y); 585 r = (x1 - x2) * (t - y2) / (y1 - y2) + x2; /* intersect P7 | P1 P2 */ 586 x0 = x; 587 x1 = ifloor(r + 0.5); 588 y0 = y1 = y; /* P0 = P6, P1 = P7 */ 589 } 590 plotQuadBezierSeg(x0, y0, x1, y1, x2, y2); /* remaining part */ 591} 592#endif 593 594static void 595plotCubicBezierSeg(int x0, int y0, 596 double x1, double y1, 597 double x2, double y2, 598 int x3, int y3) 599{ /* plot limited cubic Bezier segment */ 600 int f, fx, fy, tt; 601 int leg = 1; 602 int sx = x0 < x3 ? 1 : -1; 603 int sy = y0 < y3 ? 1 : -1; /* step direction */ 604 double xc = -fabs(x0 + x1 - x2 - x3); 605 double xa = xc - 4 * sx * (x1 - x2); 606 double xb = sx * (x0 - x1 - x2 + x3); 607 double yc = -fabs(y0 + y1 - y2 - y3); 608 double ya = yc - 4 * sy * (y1 - y2); 609 double yb = sy * (y0 - y1 - y2 + y3); 610 double ab, ac, bc, cb, xx, xy, yy, dx, dy, ex, *pxy; 611 double EP = 0.01; 612 /* check for curve restrains */ 613 /* slope P0-P1 == P2-P3 and (P0-P3 == P1-P2 or no slope change) */ 614 assert((x1 - x0) * (x2 - x3) < EP && 615 ((x3 - x0) * (x1 - x2) < EP || xb * xb < xa * xc + EP)); 616 assert((y1 - y0) * (y2 - y3) < EP && 617 ((y3 - y0) * (y1 - y2) < EP || yb * yb < ya * yc + EP)); 618 619 if (xa == 0 && ya == 0) { /* quadratic Bezier */ 620 sx = ifloor((3 * x1 - x0 + 1) / 2); 621 sy = ifloor((3 * y1 - y0 + 1) / 2); /* new midpoint */ 622 plotQuadBezierSeg(x0, y0, sx, sy, x3, y3); 623 return; 624 } 625 x1 = (x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0) + 1; /* line lengths */ 626 x2 = (x2 - x3) * (x2 - x3) + (y2 - y3) * (y2 - y3) + 1; 627 do { /* loop over both ends */ 628 ab = xa * yb - xb * ya; 629 ac = xa * yc - xc * ya; 630 bc = xb * yc - xc * yb; 631 ex = ab * (ab + ac - 3 * bc) + ac * ac; /* P0 part of self-intersection loop? */ 632 f = ((ex > 0.0) 633 ? 1 634 : isqrt(1 + 1024 / x1)); /* calculate resolution */ 635 ab *= f; 636 ac *= f; 637 bc *= f; 638 ex *= f * f; /* increase resolution */ 639 xy = 9 * (ab + ac + bc) / 8; 640 cb = 8 * (xa - ya); /* init differences of 1st degree */ 641 dx = 27 * (8 * ab * (yb * yb - ya * yc) + 642 ex * (ya + 2 * yb + yc)) / 64 - ya * ya * (xy - ya); 643 dy = 27 * (8 * ab * (xb * xb - xa * xc) - 644 ex * (xa + 2 * xb + xc)) / 64 - xa * xa * (xy + xa); 645 /* init differences of 2nd degree */ 646 xx = 3 * (3 * ab * (3 * yb * yb - ya * ya - 2 * ya * yc) - 647 ya * (3 * ac * (ya + yb) + ya * cb)) / 4; 648 yy = 3 * (3 * ab * (3 * xb * xb - xa * xa - 2 * xa * xc) - 649 xa * (3 * ac * (xa + xb) + xa * cb)) / 4; 650 xy = xa * ya * (6 * ab + 6 * ac - 3 * bc + cb); 651 ac = ya * ya; 652 cb = xa * xa; 653 xy = 3 * (xy + 9 * f * (cb * yb * yc - xb * xc * ac) - 654 18 * xb * yb * ab) / 8; 655 656 if (ex < 0) { /* negate values if inside self-intersection loop */ 657 dx = -dx; 658 dy = -dy; 659 xx = -xx; 660 yy = -yy; 661 xy = -xy; 662 ac = -ac; 663 cb = -cb; 664 } /* init differences of 3rd degree */ 665 ab = 6 * ya * ac; 666 ac = -6 * xa * ac; 667 bc = 6 * ya * cb; 668 cb = -6 * xa * cb; 669 dx += xy; 670 ex = dx + dy; 671 dy += xy; /* error of 1st step */ 672 673 for (pxy = &xy, fx = fy = f; x0 != x3 && y0 != y3;) { 674 setPixel(x0, y0); /* plot curve */ 675 do { /* move sub-steps of one pixel */ 676 if (dx > *pxy || dy < *pxy) 677 goto exit; /* confusing values */ 678 y1 = 2 * ex - dy; /* save value for test of y step */ 679 if (2 * ex >= dx) { /* x sub-step */ 680 fx--; 681 ex += dx += xx; 682 dy += xy += ac; 683 yy += bc; 684 xx += ab; 685 } 686 if (y1 <= 0) { /* y sub-step */ 687 fy--; 688 ex += dy += yy; 689 dx += xy += bc; 690 xx += ac; 691 yy += cb; 692 } 693 } while (fx > 0 && fy > 0); /* pixel complete? */ 694 if (2 * fx <= f) { 695 x0 += sx; 696 fx += f; 697 } /* x step */ 698 if (2 * fy <= f) { 699 y0 += sy; 700 fy += f; 701 } /* y step */ 702 if (pxy == &xy && dx < 0 && dy > 0) 703 pxy = &EP; /* pixel ahead valid */ 704 } 705 exit: 706 EXCHANGE(x0, x3, tt); 707 sx = -sx; 708 xb = -xb; /* swap legs */ 709 EXCHANGE(y0, y3, tt); 710 sy = -sy; 711 yb = -yb; 712 x1 = x2; 713 } while (leg--); /* try other end */ 714 plotLine(x0, y0, x3, y3); /* remaining part in case of cusp or crunode */ 715} 716 717static void 718plotCubicBezier(int x0, int y0, int x1, int y1, 719 int x2, int y2, int x3, int y3) 720{ /* plot any cubic Bezier curve */ 721 int n = 0, i = 0; 722 long xc = x0 + x1 - x2 - x3; 723 long xa = xc - 4 * (x1 - x2); 724 long xb = x0 - x1 - x2 + x3; 725 long xd = xb + 4 * (x1 + x2); 726 long yc = y0 + y1 - y2 - y3; 727 long ya = yc - 4 * (y1 - y2); 728 long yb = y0 - y1 - y2 + y3; 729 long yd = yb + 4 * (y1 + y2); 730 double fx0 = x0, fx1, fx2, fx3, fy0 = y0, fy1, fy2, fy3; 731 double t1 = (double) (xb * xb - xa * xc), t2, t[5]; 732 733#ifdef DEBUG_BEZIER 734 printf("plotCubicBezier(%d,%d, %d,%d, %d,%d, %d,%d\n", 735 x0, y0, x1, y1, x2, y2, x3, y3); 736#endif 737 /* sub-divide curve at gradient sign changes */ 738 if (xa == 0) { /* horizontal */ 739 if (labs(xc) < 2 * labs(xb)) 740 t[n++] = (double) xc / (2.0 * (double) xb); /* one change */ 741 } else if (t1 > 0.0) { /* two changes */ 742 t2 = sqrt(t1); 743 t1 = ((double) xb - t2) / (double) xa; 744 if (fabs(t1) < 1.0) 745 t[n++] = t1; 746 t1 = ((double) xb + t2) / (double) xa; 747 if (fabs(t1) < 1.0) 748 t[n++] = t1; 749 } 750 t1 = (double) (yb * yb - ya * yc); 751 if (ya == 0) { /* vertical */ 752 if (labs(yc) < 2 * labs(yb)) 753 t[n++] = (double) yc / (2.0 * (double) yb); /* one change */ 754 } else if (t1 > 0.0) { /* two changes */ 755 t2 = sqrt(t1); 756 t1 = ((double) yb - t2) / (double) ya; 757 if (fabs(t1) < 1.0) 758 t[n++] = t1; 759 t1 = ((double) yb + t2) / (double) ya; 760 if (fabs(t1) < 1.0) 761 t[n++] = t1; 762 } 763 for (i = 1; i < n; i++) /* bubble sort of 4 points */ 764 if ((t1 = t[i - 1]) > t[i]) { 765 t[i - 1] = t[i]; 766 t[i] = t1; 767 i = 0; 768 } 769 770 t1 = -1.0; 771 t[n] = 1.0; /* begin / end point */ 772 for (i = 0; i <= n; i++) { /* plot each segment separately */ 773 t2 = t[i]; /* sub-divide at t[i-1], t[i] */ 774 fx1 = (t1 * (t1 * (double) xb - (double) (2 * xc)) - 775 t2 * (t1 * (t1 * (double) xa - (double) (2 * xb)) + (double) 776 xc) + (double) xd) / 8 - fx0; 777 fy1 = (t1 * (t1 * (double) yb - (double) (2 * yc)) - 778 t2 * (t1 * (t1 * (double) ya - (double) (2 * yb)) + (double) 779 yc) + (double) yd) / 8 - fy0; 780 fx2 = (t2 * (t2 * (double) xb - (double) (2 * xc)) - 781 t1 * (t2 * (t2 * (double) xa - (double) (2 * xb)) + (double) 782 xc) + (double) xd) / 8 - fx0; 783 fy2 = (t2 * (t2 * (double) yb - (double) (2 * yc)) - 784 t1 * (t2 * (t2 * (double) ya - (double) (2 * yb)) + (double) 785 yc) + (double) yd) / 8 - fy0; 786 fx0 -= fx3 = (t2 * (t2 * ((double) (3 * xb) - t2 * (double) xa) - 787 (double) (3 * xc)) + (double) xd) / 8; 788 fy0 -= fy3 = (t2 * (t2 * ((double) (3 * yb) - t2 * (double) ya) - 789 (double) (3 * yc)) + (double) yd) / 8; 790 x3 = ifloor(fx3 + 0.5); 791 y3 = ifloor(fy3 + 0.5); /* scale bounds to int */ 792 if (fx0 != 0.0) { 793 fx1 *= fx0 = (x0 - x3) / fx0; 794 fx2 *= fx0; 795 } 796 if (fy0 != 0.0) { 797 fy1 *= fy0 = (y0 - y3) / fy0; 798 fy2 *= fy0; 799 } 800 if (x0 != x3 || y0 != y3) /* segment t1 - t2 */ 801 plotCubicBezierSeg(x0, y0, 802 x0 + fx1, y0 + fy1, 803 x0 + fx2, y0 + fy2, 804 x3, y3); 805 x0 = x3; 806 y0 = y3; 807 fx0 = fx3; 808 fy0 = fy3; 809 t1 = t2; 810 } 811} 812 813#if 0 814static void 815plotQuadSpline(int n, int x[], int y[], int skip_segments) 816{ /* plot quadratic spline, destroys input arrays x,y */ 817#define M_MAX 12 818 double mi = 1, m[M_MAX]; /* diagonal constants of matrix */ 819 int i, x0, y0, x1, y1, x2, y2; 820#ifdef DEBUG_SPLINE_SEGMENTS 821 int color = 0; 822#endif 823 824 assert(n > 1); /* need at least 3 points P[0]..P[n] */ 825 826#ifdef DEBUG_SPLINE_POINTS 827 { 828 int save_pattern; 829 830 i = 0; 831 global_context->temporary_write_controls.foreground = 11; 832 save_pattern = global_context->temporary_write_controls.pattern; 833 global_context->temporary_write_controls.pattern = 0xff; 834 draw_patterned_arc(global_context, x[i], y[i], x[i] + 2, y[i], 0, 835 360, NULL, NULL); 836 i++; 837 global_context->temporary_write_controls.foreground = 15; 838 for (; i < n; i++) { 839 draw_patterned_arc(global_context, 840 x[i], y[i], 841 x[i] + 2, y[i], 842 0, 360, NULL, NULL); 843 } 844 global_context->temporary_write_controls.foreground = 10; 845 draw_patterned_arc(global_context, x[i], y[n], x[i] + 2, y[i], 0, 846 360, NULL, NULL); 847 global_context->temporary_write_controls.pattern = save_pattern; 848 } 849#endif 850 851 x2 = x[n]; 852 y2 = y[n]; 853 854 x[1] = x0 = 8 * x[1] - 2 * x[0]; /* first row of matrix */ 855 y[1] = y0 = 8 * y[1] - 2 * y[0]; 856 857 for (i = 2; i < n; i++) { /* forward sweep */ 858 if (i - 2 < M_MAX) 859 m[i - 2] = mi = 1.0 / (6.0 - mi); 860 x[i] = x0 = ifloor(8 * x[i] - x0 * mi + 0.5); /* store yi */ 861 y[i] = y0 = ifloor(8 * y[i] - y0 * mi + 0.5); 862 } 863 x1 = ifloor((x0 - 2 * x2) / (5.0 - mi) + 0.5); /* correction last row */ 864 y1 = ifloor((y0 - 2 * y2) / (5.0 - mi) + 0.5); 865 866 for (i = n - 2; i > 0; i--) { /* back substitution */ 867 if (i <= M_MAX) 868 mi = m[i - 1]; 869 x0 = ifloor((x[i] - x1) * mi + 0.5); /* next corner */ 870 y0 = ifloor((y[i] - y1) * mi + 0.5); 871#ifdef DEBUG_SPLINE_SEGMENTS 872 color++; 873 global_context->temporary_write_controls.foreground = color; 874#endif 875 if ((n - 2) - i < skip_segments) 876 plotQuadBezier((x0 + x1) / 2, (y0 + y1) / 2, x1, y1, x2, y2); 877 x2 = (x0 + x1) / 2; 878 x1 = x0; 879 y2 = (y0 + y1) / 2; 880 y1 = y0; 881 } 882#ifdef DEBUG_SPLINE_SEGMENTS 883 color++; 884 global_context->temporary_write_controls.foreground = color; 885#endif 886 if (skip_segments > 0) 887 plotQuadBezier(x[0], y[0], x1, y1, x2, y2); 888} 889#endif 890 891static void 892plotCubicSpline(int n, int x[], int y[], int skip_first_last) 893{ 894#define M_MAX 12 895 double mi = 0.25, m[M_MAX]; /* diagonal constants of matrix */ 896 int x3, y3, x4, y4; 897 int i, x0, y0, x1, y1, x2, y2; 898#ifdef DEBUG_SPLINE_SEGMENTS 899 int color = 0; 900#endif 901 902 assert(n > 2); /* need at least 4 points P[0]..P[n] */ 903 904#ifdef DEBUG_SPLINE_POINTS 905 { 906 int save_pattern; 907 908 i = 0; 909 global_context->temporary_write_controls.foreground = 11; 910 save_pattern = global_context->temporary_write_controls.pattern; 911 global_context->temporary_write_controls.pattern = 0xff; 912 draw_patterned_arc(global_context, x[i], y[i], x[i] + 2, y[i], 0, 913 360, NULL, NULL); 914 i++; 915 global_context->temporary_write_controls.foreground = 15; 916 for (; i < n; i++) { 917 draw_patterned_arc(global_context, 918 x[i], y[i], 919 x[i] + 2, y[i], 920 0, 360, NULL, NULL); 921 } 922 global_context->temporary_write_controls.foreground = 10; 923 draw_patterned_arc(global_context, x[i], y[i], x[i] + 2, y[i], 0, 924 360, NULL, NULL); 925 global_context->temporary_write_controls.pattern = save_pattern; 926 } 927#endif 928 929 x3 = x[n - 1]; 930 y3 = y[n - 1]; 931 x4 = x[n]; 932 y4 = y[n]; 933 934 x[1] = x0 = 12 * x[1] - 3 * x[0]; /* first row of matrix */ 935 y[1] = y0 = 12 * y[1] - 3 * y[0]; 936 937 for (i = 2; i < n; i++) { /* forward sweep */ 938 if (i - 2 < M_MAX) 939 m[i - 2] = mi = 0.25 / (2.0 - mi); 940 x[i] = x0 = ifloor(12 * x[i] - 2 * x0 * mi + 0.5); 941 y[i] = y0 = ifloor(12 * y[i] - 2 * y0 * mi + 0.5); 942 } 943 x2 = ifloor((x0 - 3 * x4) / (7 - 4 * mi) + 0.5); /* correct last row */ 944 printf("y0=%d, y4=%d mi=%g\n", y0, y4, mi); 945 y2 = ifloor((y0 - 3 * y4) / (7 - 4 * mi) + 0.5); 946 printf("y2=%d, y3=%d, y4=%d\n", y2, y3, y4); 947#ifdef DEBUG_SPLINE_SEGMENTS 948 color++; 949 global_context->temporary_write_controls.foreground = color; 950#endif 951 if (!skip_first_last) 952 plotCubicBezier(x3, y3, (x2 + x4) / 2, (y2 + y4) / 2, x4, y4, x4, y4); 953 954 if (n - 3 < M_MAX) 955 mi = m[n - 3]; 956 x1 = ifloor((x[n - 2] - 2 * x2) * mi + 0.5); 957 y1 = ifloor((y[n - 2] - 2 * y2) * mi + 0.5); 958 for (i = n - 3; i > 0; i--) { /* back substitution */ 959 if (i <= M_MAX) 960 mi = m[i - 1]; 961 x0 = ifloor((x[i] - 2 * x1) * mi + 0.5); 962 y0 = ifloor((y[i] - 2 * y1) * mi + 0.5); 963 x4 = ifloor((x0 + 4 * x1 + x2 + 3) / 6.0); /* reconstruct P[i] */ 964 y4 = ifloor((y0 + 4 * y1 + y2 + 3) / 6.0); 965#ifdef DEBUG_SPLINE_SEGMENTS 966 color++; 967 global_context->temporary_write_controls.foreground = color; 968#endif 969 plotCubicBezier(x4, y4, 970 ifloor((2 * x1 + x2) / 3 + 0.5), 971 ifloor((2 * y1 + y2) / 3 + 0.5), 972 ifloor((x1 + 2 * x2) / 3 + 0.5), 973 ifloor((y1 + 2 * y2) / 3 + 0.5), 974 x3, y3); 975 x3 = x4; 976 y3 = y4; 977 x2 = x1; 978 y2 = y1; 979 x1 = x0; 980 y1 = y0; 981 } 982 x0 = x[0]; 983 x4 = ifloor((3 * x0 + 7 * x1 + 2 * x2 + 6) / 12.0); /* reconstruct P[1] */ 984 y0 = y[0]; 985 y4 = ifloor((3 * y0 + 7 * y1 + 2 * y2 + 6) / 12.0); 986#ifdef DEBUG_SPLINE_SEGMENTS 987 global_context->temporary_write_controls.foreground = 4; 988#endif 989 plotCubicBezier(x4, y4, 990 ifloor((2 * x1 + x2) / 3 + 0.5), 991 ifloor((2 * y1 + y2) / 3 + 0.5), 992 ifloor((x1 + 2 * x2) / 3 + 0.5), 993 ifloor((y1 + 2 * y2) / 3 + 0.5), 994 x3, y3); 995#ifdef DEBUG_SPLINE_SEGMENTS 996 color++; 997 global_context->temporary_write_controls.foreground = color; 998#endif 999 if (!skip_first_last) 1000 plotCubicBezier(x0, y0, x0, y0, (x0 + x1) / 2, (y0 + y1) / 2, x4, y4); 1001} 1002 1003static void 1004init_fragment(RegisDataFragment *fragment, char const *str) 1005{ 1006 fragment->start = str; 1007 fragment->len = (unsigned) strlen(str); 1008 fragment->pos = 0U; 1009} 1010 1011static void 1012copy_fragment(RegisDataFragment *dst, RegisDataFragment const *src) 1013{ 1014 dst->start = src->start; 1015 dst->len = src->len; 1016 dst->pos = src->pos; 1017} 1018 1019static char 1020peek_fragment(RegisDataFragment const *fragment) 1021{ 1022 if (fragment->pos < fragment->len) { 1023 return fragment->start[fragment->pos]; 1024 } 1025 return '\0'; 1026} 1027 1028static char 1029pop_fragment(RegisDataFragment *fragment) 1030{ 1031 if (fragment->pos < fragment->len) { 1032 return fragment->start[fragment->pos++]; 1033 } 1034 return '\0'; 1035} 1036 1037static size_t 1038fragment_len(RegisDataFragment const *fragment) 1039{ 1040 return fragment->len - fragment->pos; 1041} 1042 1043#define MAX_FRAG 1024 1044static char const * 1045fragment_to_tempstr(RegisDataFragment const *fragment) 1046{ 1047 static char tempstr[MAX_FRAG + 1]; 1048 size_t remaininglen = fragment_len(fragment); 1049 size_t minlen = ((remaininglen < MAX_FRAG) 1050 ? remaininglen 1051 : MAX_FRAG); 1052 1053 (void) strncpy(tempstr, &fragment->start[fragment->pos], minlen); 1054 tempstr[minlen] = '\0'; 1055 return tempstr; 1056} 1057 1058static int 1059skip_regis_whitespace(RegisDataFragment *input) 1060{ 1061 int skipped = 0; 1062 char ch; 1063 1064 assert(input); 1065 1066 for (; input->pos < input->len; input->pos++) { 1067 /* FIXME: the semicolon isn't whitespace -- it also terminates the current command even if inside of an optionset or extent */ 1068 ch = input->start[input->pos]; 1069 if (ch != ',' && ch != ';' && !IsSpace(ch)) { 1070 break; 1071 } 1072 if (ch == '\n') { 1073 TRACE(("end of input line\n\n")); 1074 } 1075 skipped = 1; 1076 } 1077 1078 if (skipped) 1079 return 1; 1080 return 0; 1081} 1082 1083static int 1084extract_regis_extent(RegisDataFragment *input, RegisDataFragment *output) 1085{ 1086 char ch; 1087 1088 assert(input); 1089 assert(output); 1090 1091 output->start = &input->start[input->pos]; 1092 output->len = 0U; 1093 output->pos = 0U; 1094 1095 if (input->pos >= input->len) 1096 return 0; 1097 1098 ch = input->start[input->pos]; 1099 if (ch != '[') 1100 return 0; 1101 input->pos++; 1102 output->start++; 1103 1104 /* FIXME: truncate to 16 bit signed integers */ 1105 for (; input->pos < input->len; input->pos++, output->len++) { 1106 ch = input->start[input->pos]; 1107 if (ch == ';') { 1108 TRACE(("DATA_ERROR: end of input before closing bracket\n")); 1109 break; 1110 } 1111 if (ch == ']') 1112 break; 1113 } 1114 if (ch == ']') 1115 input->pos++; 1116 1117 return 1; 1118} 1119 1120static int 1121extract_regis_num(RegisDataFragment *input, RegisDataFragment *output) 1122{ 1123 char ch = 0; 1124 int has_digits = 0; 1125 1126 assert(input); 1127 assert(output); 1128 1129 output->start = &input->start[input->pos]; 1130 output->len = 0U; 1131 output->pos = 0U; 1132 1133 if (input->start[input->pos] == '-' || 1134 input->start[input->pos] == '+') { 1135 input->pos++; 1136 output->len++; 1137 } 1138 1139 for (; input->pos < input->len; input->pos++, output->len++) { 1140 ch = input->start[input->pos]; 1141 if (ch != '0' && ch != '1' && ch != '2' && ch != '3' && 1142 ch != '4' && ch != '5' && ch != '6' && ch != '7' && 1143 ch != '8' && ch != '9') { 1144 break; 1145 } 1146 has_digits = 1; 1147 } 1148 1149 /* FIXME: what degenerate forms should be accepted ("E10" "1E" "1e" "1." "1ee10")? */ 1150 /* FIXME: the terminal is said to support "floating point values", truncating to int... what do these look like? */ 1151 if (has_digits && ch == 'E') { 1152 input->pos++; 1153 output->len++; 1154 for (; input->pos < input->len; input->pos++, output->len++) { 1155 ch = input->start[input->pos]; 1156 if (ch != '0' && ch != '1' && ch != '2' && ch != '3' && 1157 ch != '4' && ch != '5' && ch != '6' && ch != '7' && 1158 ch != '8' && ch != '9') { 1159 break; 1160 } 1161 } 1162 } 1163 1164 return has_digits; 1165} 1166 1167static int 1168extract_regis_pixelvector(RegisDataFragment *input, RegisDataFragment *output) 1169{ 1170 char ch; 1171 int has_digits; 1172 1173 assert(input); 1174 assert(output); 1175 1176 output->start = &input->start[input->pos]; 1177 output->len = 0U; 1178 output->pos = 0U; 1179 1180 if (input->pos < input->len) { 1181 ch = input->start[input->pos]; 1182 if (ch == '+' || ch == '-') { 1183 input->pos++; 1184 output->len++; 1185 } 1186 } 1187 1188 has_digits = 0; 1189 for (; input->pos < input->len; input->pos++, output->len++) { 1190 ch = input->start[input->pos]; 1191 if (ch != '0' && ch != '1' && ch != '2' && ch != '3' && 1192 ch != '4' && ch != '5' && ch != '6' && ch != '7') { 1193 break; 1194 } 1195 has_digits = 1; 1196 } 1197 1198 return has_digits; 1199} 1200 1201static int 1202extract_regis_command(RegisDataFragment *input, char *command) 1203{ 1204 char ch; 1205 1206 assert(input); 1207 assert(command); 1208 1209 if (input->pos >= input->len) 1210 return 0; 1211 1212 ch = input->start[input->pos]; 1213 if (ch == '\0' || ch == ';') { 1214 return 0; 1215 } 1216 if (!islower(CharOf(ch)) && !isupper(CharOf(ch))) { 1217 return 0; 1218 } 1219 *command = ch; 1220 input->pos++; 1221 1222 return 1; 1223} 1224 1225static int 1226extract_regis_string(RegisDataFragment *input, char *out, unsigned maxlen) 1227{ 1228 char first_ch; 1229 char ch; 1230 char prev_ch; 1231 unsigned outlen = 0U; 1232 1233 assert(input); 1234 assert(out); 1235 1236 if (input->pos >= input->len) 1237 return 0; 1238 1239 ch = input->start[input->pos]; 1240 if (ch != '\'' && ch != '"') 1241 return 0; 1242 first_ch = ch; 1243 input->pos++; 1244 1245 ch = '\0'; 1246 for (; input->pos < input->len; input->pos++) { 1247 prev_ch = ch; 1248 ch = input->start[input->pos]; 1249 /* ';' (resync) is not recognized in strings */ 1250 if (prev_ch == first_ch) { 1251 if (ch == first_ch) { 1252 if (outlen < maxlen) { 1253 out[outlen] = ch; 1254 } 1255 outlen++; 1256 ch = '\0'; 1257 continue; 1258 } 1259 if (outlen < maxlen) 1260 out[outlen] = '\0'; 1261 else 1262 out[maxlen] = '\0'; 1263 return 1; 1264 } 1265 if (ch == '\0') 1266 break; 1267 if (ch != first_ch) { 1268 if (outlen < maxlen) { 1269 out[outlen] = ch; 1270 } 1271 outlen++; 1272 } 1273 } 1274 if (ch == first_ch) { 1275 if (outlen < maxlen) 1276 out[outlen] = '\0'; 1277 else 1278 out[maxlen] = '\0'; 1279 return 1; 1280 } 1281 /* FIXME: handle multiple strings concatenated with commas */ 1282 1283 TRACE(("DATA_ERROR: end of input during before closing quote\n")); 1284 return 0; 1285} 1286 1287static int 1288extract_regis_optionset(RegisDataFragment *input, RegisDataFragment *output) 1289{ 1290 char ch; 1291 int nesting; 1292 1293 assert(input); 1294 assert(output); 1295 1296 output->start = &input->start[input->pos]; 1297 output->len = 0U; 1298 output->pos = 0U; 1299 1300 if (input->pos >= input->len) 1301 return 0; 1302 1303 ch = input->start[input->pos]; 1304 if (ch != '(') 1305 return 0; 1306 input->pos++; 1307 output->start++; 1308 nesting = 1; 1309 1310 /* FIXME: handle strings with parens */ 1311 for (; input->pos < input->len; input->pos++, output->len++) { 1312 ch = input->start[input->pos]; 1313 if (ch == ';') 1314 break; 1315 if (ch == '(') 1316 nesting++; 1317 if (ch == ')') { 1318 nesting--; 1319 if (nesting == 0) { 1320 input->pos++; 1321 return 1; 1322 } 1323 } 1324 } 1325 1326 TRACE(("DATA_ERROR: end of input before closing paren (%d levels deep)\n", nesting)); 1327 return 0; 1328} 1329 1330static int 1331extract_regis_option(RegisDataFragment *input, 1332 char *option, 1333 RegisDataFragment *output) 1334{ 1335 char ch; 1336 int nesting; 1337 1338 assert(input); 1339 assert(option); 1340 assert(output); 1341 1342 /* LETTER suboptions* value? */ 1343 /* FIXME: can there be whitespace or commas inside of an option? */ 1344 /* FIXME: what are the rules for using separate parens vs. sharing between options? */ 1345 1346 output->start = &input->start[input->pos]; 1347 output->len = 0U; 1348 output->pos = 0U; 1349 1350 if (input->pos >= input->len) { 1351 return 0; 1352 } 1353 1354 ch = input->start[input->pos]; 1355 if (ch == ';' || ch == ',' || ch == '(' || ch == ')' || isdigit(CharOf(ch))) { 1356 return 0; 1357 } 1358 *option = ch; 1359 input->pos++; 1360 output->start++; 1361 nesting = 0; 1362 1363 /* FIXME: handle strings with parens, nested parens, etc. */ 1364 for (; input->pos < input->len; input->pos++, output->len++) { 1365 ch = input->start[input->pos]; 1366 TRACE(("looking at option char %c\n", ch)); 1367 /* FIXME: any special rules for commas? any need to track parens? */ 1368 if (ch == '(') { 1369 TRACE(("nesting++\n")); 1370 nesting++; 1371 } 1372 if (ch == ')') { 1373 TRACE(("nesting--\n")); 1374 nesting--; 1375 if (nesting < 0) { 1376 TRACE(("DATA_ERROR: found ReGIS option has value with too many close parens \"%c\"\n", *option)); 1377 return 0; 1378 } 1379 } 1380 /* top-level commas indicate the end of this option and the start of another */ 1381 if (nesting == 0 && ch == ',') 1382 break; 1383 if (ch == ';') 1384 break; 1385 } 1386 if (nesting != 0) { 1387 TRACE(("DATA_ERROR: mismatched parens in argument to ReGIS option \"%c\"\n", *option)); 1388 return 0; 1389 } 1390 1391 TRACE(("found ReGIS option and value \"%c\" \"%s\"\n", 1392 *option, 1393 fragment_to_tempstr(output))); 1394 return 1; 1395} 1396 1397static int 1398regis_num_to_int(RegisDataFragment const *input, int *out) 1399{ 1400 char ch; 1401 1402 /* FIXME: handle exponential notation and rounding */ 1403 /* FIXME: check for junk after the number */ 1404 ch = peek_fragment(input); 1405 if (ch != '0' && 1406 ch != '1' && 1407 ch != '2' && 1408 ch != '3' && 1409 ch != '4' && 1410 ch != '5' && 1411 ch != '6' && 1412 ch != '7' && 1413 ch != '8' && 1414 ch != '9' && 1415 ch != '+' && 1416 ch != '-') { 1417 return 0; 1418 } 1419 1420 TRACE(("converting \"%s\" to an int\n", fragment_to_tempstr(input))); 1421 *out = atoi(fragment_to_tempstr(input)); 1422 return 1; 1423} 1424 1425static int 1426load_regis_colorspec(Graphic const *graphic, RegisDataFragment const *input, RegisterNum *out) 1427{ 1428 int val; 1429 RegisDataFragment colorspec; 1430 RegisDataFragment coloroption; 1431 1432 copy_fragment(&colorspec, input); 1433 TRACE(("looking at colorspec pattern: \"%s\"\n", fragment_to_tempstr(&colorspec))); 1434 1435 if (regis_num_to_int(&colorspec, &val)) { 1436 if (val < 0 || val >= (int) graphic->valid_registers) { /* FIXME: wrap? */ 1437 TRACE(("DATA_ERROR: colorspec value %d\n", val)); 1438 return 0; 1439 } 1440 TRACE(("colorspec contains index for register %u\n", val)); 1441 *out = (RegisterNum) val; 1442 return 1; 1443 } 1444 1445 if (extract_regis_optionset(&colorspec, &coloroption)) { 1446 short r, g, b; 1447 TRACE(("option: \"%s\"\n", fragment_to_tempstr(&coloroption))); 1448 1449 if (fragment_len(&coloroption) == 1) { 1450 char ch = pop_fragment(&coloroption); 1451 1452 TRACE(("got regis RGB colorspec pattern: \"%s\"\n", 1453 fragment_to_tempstr(&coloroption))); 1454 switch (ch) { 1455 case 'D': 1456 case 'd': 1457 r = 0; 1458 g = 0; 1459 b = 0; 1460 break; 1461 case 'R': 1462 case 'r': 1463 r = 100; 1464 g = 0; 1465 b = 0; 1466 break; 1467 case 'G': 1468 case 'g': 1469 r = 0; 1470 g = 100; 1471 b = 0; 1472 break; 1473 case 'B': 1474 case 'b': 1475 r = 0; 1476 g = 0; 1477 b = 100; 1478 break; 1479 case 'C': 1480 case 'c': 1481 r = 0; 1482 g = 100; 1483 b = 100; 1484 break; 1485 case 'Y': 1486 case 'y': 1487 r = 100; 1488 g = 100; 1489 b = 0; 1490 break; 1491 case 'M': 1492 case 'm': 1493 r = 100; 1494 g = 0; 1495 b = 100; 1496 break; 1497 case 'W': 1498 case 'w': 1499 r = 100; 1500 g = 100; 1501 b = 100; 1502 break; 1503 default: 1504 TRACE(("unknown RGB color name: \"%c\"\n", ch)); 1505 return 0; 1506 } 1507 } else { 1508 short h, l, s; 1509 1510 if (sscanf(fragment_to_tempstr(&coloroption), 1511 "%*1[Hh]%hd%*1[Ll]%hd%*1[Ss]%hd", 1512 &h, &l, &s) != 3) { 1513 TRACE(("unrecognized colorspec format: \"%s\"\n", 1514 fragment_to_tempstr(&coloroption))); 1515 return 0; 1516 } 1517 hls2rgb(h, l, s, &r, &g, &b); 1518 } 1519 /* FIXME: check for trailing junk? */ 1520 *out = find_color_register(graphic->color_registers, r, g, b); 1521 TRACE(("colorspec maps to closest register %u\n", *out)); 1522 return 1; 1523 } 1524 1525 TRACE(("unrecognized colorspec format: \"%s\"\n", fragment_to_tempstr(&colorspec))); 1526 return 0; 1527} 1528 1529static int 1530load_regis_extent(char const *extent, int origx, int origy, int *xloc, int *yloc) 1531{ 1532 int xsign, ysign; 1533 char const *xpart; 1534 char const *ypart; 1535 1536 xpart = extent; 1537 if ((ypart = strchr(extent, ','))) { 1538 ypart++; 1539 } else { 1540 ypart = ""; 1541 } 1542 1543 if (xpart[0] == '-') { 1544 xsign = -1; 1545 xpart++; 1546 } else if (xpart[0] == '+') { 1547 xsign = +1; 1548 xpart++; 1549 } else { 1550 xsign = 0; 1551 } 1552 if (ypart[0] == '-') { 1553 ysign = -1; 1554 ypart++; 1555 } else if (ypart[0] == '+') { 1556 ysign = +1; 1557 ypart++; 1558 } else { 1559 ysign = 0; 1560 } 1561 1562 if (xpart[0] == '\0' || xpart[0] == ',') { 1563 *xloc = origx; 1564 } else if (xsign == 0) { 1565 *xloc = atoi(xpart); 1566 } else { 1567 *xloc = origx + xsign * atoi(xpart); 1568 } 1569 if (ypart[0] == '\0') { 1570 *yloc = origy; 1571 } else if (ysign == 0) { 1572 *yloc = atoi(ypart); 1573 } else { 1574 *yloc = origy + ysign * atoi(ypart); 1575 } 1576 1577 return 1; 1578} 1579 1580static int 1581load_regis_pixelvector(char const *pixelvector, 1582 unsigned mul, 1583 int origx, int origy, 1584 int *xloc, int *yloc) 1585{ 1586 int dx = 0, dy = 0; 1587 int i; 1588 1589 for (i = 0; pixelvector[i] != '\0'; i++) { 1590 switch (pixelvector[i]) { 1591 case '0': 1592 dx += 1; 1593 break; 1594 case '1': 1595 dx += 1; 1596 dy -= 1; 1597 break; 1598 case '2': 1599 dy -= 1; 1600 break; 1601 case '3': 1602 dx -= 1; 1603 dy -= 1; 1604 break; 1605 case '4': 1606 dx -= 1; 1607 break; 1608 case '5': 1609 dx -= 1; 1610 dy += 1; 1611 break; 1612 case '6': 1613 dy += 1; 1614 break; 1615 case '7': 1616 dx += 1; 1617 dy += 1; 1618 break; 1619 default: 1620 break; 1621 } 1622 } 1623 1624 *xloc = origx + dx * (int) mul; 1625 *yloc = origy + dy * (int) mul; 1626 1627 return 1; 1628} 1629 1630static int 1631load_regis_write_control(RegisParseState *state, 1632 Graphic const *graphic, 1633 int cur_x, int cur_y, 1634 int option, 1635 RegisDataFragment *arg, 1636 RegisWriteControls *out) 1637{ 1638 TRACE(("checking write control option \"%c\" with arg \"%s\"\n", 1639 option, fragment_to_tempstr(arg))); 1640 switch (option) { 1641 case 'E': 1642 case 'e': 1643 TRACE(("write control erase writing mode \"%s\"\n", 1644 fragment_to_tempstr(arg))); 1645 out->write_style = WRITE_STYLE_ERASE; 1646 break; 1647 case 'F': 1648 case 'f': 1649 TRACE(("write control plane write mask \"%s\"\n", 1650 fragment_to_tempstr(arg))); 1651 { 1652 int val; 1653 if (!regis_num_to_int(arg, &val) || 1654 val < 0 || val >= (int) graphic->valid_registers) { 1655 TRACE(("interpreting out of range value as 0 FIXME\n")); 1656 out->plane_mask = 0U; 1657 } else { 1658 out->plane_mask = (unsigned) val; 1659 } 1660 } 1661 break; 1662 case 'I': 1663 case 'i': 1664 TRACE(("write control foreground color \"%s\"\n", 1665 fragment_to_tempstr(arg))); 1666 if (!load_regis_colorspec(graphic, arg, &out->foreground)) { 1667 TRACE(("DATA_ERROR: write control foreground color specifier not recognized: \"%s\"\n", 1668 fragment_to_tempstr(arg))); 1669 return 0; 1670 } 1671 break; 1672 case 'M': 1673 case 'm': 1674 TRACE(("write control found pixel multiplication factor \"%s\"\n", 1675 fragment_to_tempstr(arg))); 1676 { 1677 int val; 1678 if (!regis_num_to_int(arg, &val) || val <= 0) { 1679 TRACE(("interpreting out of range value %d as 1 FIXME\n", val)); 1680 out->pv_multiplier = 1U; 1681 } else { 1682 out->pv_multiplier = (unsigned) val; 1683 } 1684 } 1685 break; 1686 case 'N': 1687 case 'n': 1688 TRACE(("write control negative pattern control \"%s\"\n", 1689 fragment_to_tempstr(arg))); 1690 { 1691 int val; 1692 if (!regis_num_to_int(arg, &val)) { 1693 val = -1; 1694 } 1695 switch (val) { 1696 default: 1697 TRACE(("interpreting out of range value %d as 0 FIXME\n", val)); 1698 out->invert_pattern = 0U; 1699 break; 1700 case 0: 1701 out->invert_pattern = 0U; 1702 break; 1703 case 1: 1704 out->invert_pattern = 1U; 1705 break; 1706 } 1707 } 1708 break; 1709 case 'P': 1710 case 'p': 1711 TRACE(("write control found pattern control \"%s\"\n", 1712 fragment_to_tempstr(arg))); 1713 { 1714 RegisDataFragment suboptionset; 1715 RegisDataFragment suboptionarg; 1716 RegisDataFragment item; 1717 char suboption; 1718 1719 while (arg->pos < arg->len) { 1720 skip_regis_whitespace(arg); 1721 TRACE(("looking for option in \"%s\"\n", fragment_to_tempstr(arg))); 1722 if (extract_regis_optionset(arg, &suboptionset)) { 1723 TRACE(("got regis write pattern suboptionset: \"%s\"\n", 1724 fragment_to_tempstr(&suboptionset))); 1725 while (suboptionset.pos < suboptionset.len) { 1726 skip_regis_whitespace(&suboptionset); 1727 if (peek_fragment(&suboptionset) == ',') { 1728 pop_fragment(&suboptionset); 1729 continue; 1730 } 1731 if (extract_regis_option(&suboptionset, &suboption, &suboptionarg)) { 1732 skip_regis_whitespace(&suboptionarg); 1733 TRACE(("inspecting write pattern suboption \"%c\" with value \"%s\"\n", 1734 suboption, fragment_to_tempstr(&suboptionarg))); 1735 switch (suboption) { 1736 case 'M': 1737 case 'm': 1738 TRACE(("found pattern multiplier \"%s\"\n", 1739 fragment_to_tempstr(&suboptionarg))); 1740 { 1741 RegisDataFragment num; 1742 int val; 1743 1744 if (extract_regis_num(&suboptionarg, &num)) { 1745 if (!regis_num_to_int(&num, &val) 1746 || val < 1) { 1747 TRACE(("interpreting out of range pattern multiplier \"%s\" as 2 FIXME\n", 1748 fragment_to_tempstr(&num))); 1749 out->pattern_multiplier = 2U; 1750 } else { 1751 out->pattern_multiplier = 1752 (unsigned) val; 1753 } 1754 } 1755 skip_regis_whitespace(&suboptionarg); 1756 if (fragment_len(&suboptionarg)) { 1757 TRACE(("DATA_ERROR: unknown content after pattern multiplier \"%s\"\n", 1758 fragment_to_tempstr(&suboptionarg))); 1759 return 0; 1760 } 1761 } 1762 break; 1763 default: 1764 TRACE(("DATA_ERROR: unknown ReGIS write pattern suboption '%c' arg \"%s\"\n", 1765 suboption, fragment_to_tempstr(&suboptionarg))); 1766 return 0; 1767 } 1768 continue; 1769 } 1770 1771 TRACE(("DATA_ERROR: skipping unknown token in pattern control suboptionset (expecting option): \"%s\"\n", 1772 fragment_to_tempstr(&suboptionset))); 1773 pop_fragment(&suboptionset); 1774 } 1775 continue; 1776 } 1777 1778 TRACE(("looking for int in \"%s\"\n", fragment_to_tempstr(arg))); 1779 if (extract_regis_num(arg, &item)) { 1780 if (peek_fragment(&item) == '0' || 1781 peek_fragment(&item) == '1') { 1782 unsigned pattern = 0U; 1783 unsigned bitcount; 1784 char ch; 1785 1786 TRACE(("converting pattern bits \"%s\"\n", 1787 fragment_to_tempstr(&item))); 1788 for (bitcount = 0;; bitcount++) { 1789 ch = pop_fragment(&item); 1790 if (ch == '\0') 1791 break; 1792 switch (ch) { 1793 case '0': 1794 if (bitcount < MAX_PATTERN_BITS) { 1795 pattern <<= 1U; 1796 } 1797 break; 1798 case '1': 1799 if (bitcount < MAX_PATTERN_BITS) { 1800 pattern <<= 1U; 1801 pattern |= 1U; 1802 } 1803 break; 1804 default: 1805 TRACE(("DATA_ERROR: unknown ReGIS write pattern bit value \"%c\"\n", 1806 ch)); 1807 return 0; 1808 } 1809 } 1810 1811 if (bitcount > 0U) { 1812 unsigned extrabits; 1813 1814 for (extrabits = 0; 1815 bitcount + extrabits < MAX_PATTERN_BITS; 1816 extrabits++) { 1817 if (pattern & (1U << (bitcount - 1U))) { 1818 pattern <<= 1U; 1819 pattern |= 1U; 1820 } else { 1821 pattern <<= 1U; 1822 } 1823 } 1824 } 1825 1826 out->pattern = pattern; 1827 } else { 1828 int val; 1829 1830 TRACE(("converting pattern id \"%s\"\n", 1831 fragment_to_tempstr(&item))); 1832 if (!regis_num_to_int(&item, &val)) 1833 val = -1; 1834 switch (val) { /* FIXME: exponential allowed? */ 1835 case 0: 1836 out->pattern = 0x00; /* solid bg */ 1837 break; 1838 case 1: 1839 out->pattern = 0xff; /* solid fg */ 1840 break; 1841 case 2: 1842 out->pattern = 0xf0; /* dash */ 1843 break; 1844 case 3: 1845 out->pattern = 0xe4; /* dash dot */ 1846 break; 1847 case 4: 1848 out->pattern = 0xaa; /* dot */ 1849 break; 1850 case 5: 1851 out->pattern = 0xea; /* dash dot dot */ 1852 break; 1853 case 6: 1854 out->pattern = 0x88; /* sparse dot */ 1855 break; 1856 case 7: 1857 out->pattern = 0x84; /* asymmetric sparse dot */ 1858 break; 1859 case 8: 1860 out->pattern = 0xc8; /* sparse dash dot */ 1861 break; 1862 case 9: 1863 out->pattern = 0x86; /* sparse dot dash */ 1864 break; 1865 default: 1866 TRACE(("DATA_ERROR: unknown ReGIS standard write pattern \"%d\"\n", val)); 1867 return 0; 1868 } 1869 } 1870 1871 TRACE(("final pattern is %02x\n", out->pattern)); 1872 continue; 1873 } 1874 1875 TRACE(("DATA_ERROR: skipping unknown token in pattern suboption: \"%s\"\n", 1876 fragment_to_tempstr(arg))); 1877 pop_fragment(arg); 1878 } 1879 } 1880 break; 1881 case 'C': 1882 case 'c': 1883 TRACE(("write control compliment writing mode \"%s\"\n", 1884 fragment_to_tempstr(arg))); 1885 out->write_style = WRITE_STYLE_COMPLEMENT; 1886 break; 1887 case 'R': 1888 case 'r': 1889 TRACE(("write control switch to replacement writing mode \"%s\"\n", 1890 fragment_to_tempstr(arg))); 1891 out->write_style = WRITE_STYLE_REPLACE; 1892 break; 1893 case 'S': 1894 case 's': 1895 TRACE(("write control shading control \"%s\"\n", 1896 fragment_to_tempstr(arg))); 1897 { 1898 RegisDataFragment suboptionset; 1899 RegisDataFragment suboptionarg; 1900 RegisDataFragment item; 1901 char suboption; 1902 char shading_character = '\0'; 1903 unsigned reference_dim = WRITE_SHADING_REF_Y; 1904 int ref_x = cur_x, ref_y = cur_y; 1905 int shading_enabled = 0; 1906 1907 while (arg->pos < arg->len) { 1908 skip_regis_whitespace(arg); 1909 1910 if (extract_regis_string(arg, state->temp, state->templen)) { 1911 TRACE(("found fill char \"%s\"\n", state->temp)); 1912 /* FIXME: allow longer strings ignoring extra chars? */ 1913 if (strlen(state->temp) != 1) { 1914 TRACE(("DATA_ERROR: expected exactly one char in fill string FIXME\n")); 1915 return 0; 1916 } 1917 shading_character = state->temp[0]; 1918 TRACE(("shading character is: %d\n", (int) shading_character)); 1919 continue; 1920 } 1921 1922 if (extract_regis_optionset(arg, &suboptionset)) { 1923 skip_regis_whitespace(&suboptionset); 1924 TRACE(("got regis shading control suboptionset: \"%s\"\n", 1925 fragment_to_tempstr(&suboptionset))); 1926 while (suboptionset.pos < suboptionset.len) { 1927 if (skip_regis_whitespace(&suboptionset)) 1928 continue; 1929 if (peek_fragment(&suboptionset) == ',') { 1930 pop_fragment(&suboptionset); 1931 continue; 1932 } 1933 if (extract_regis_option(&suboptionset, &suboption, &suboptionarg)) { 1934 TRACE(("inspecting write shading suboption \"%c\" with value \"%s\"\n", 1935 suboption, fragment_to_tempstr(&suboptionarg))); 1936 switch (suboption) { 1937 case 'X': 1938 case 'x': 1939 TRACE(("found vertical shading suboption \"%s\"\n", 1940 fragment_to_tempstr(&suboptionarg))); 1941 if (fragment_len(&suboptionarg)) { 1942 TRACE(("DATA_ERROR: unexpected value to vertical shading suboption FIXME\n")); 1943 return 0; 1944 } 1945 reference_dim = WRITE_SHADING_REF_X; 1946 break; 1947 default: 1948 TRACE(("DATA_ERROR: unknown ReGIS write pattern suboption '%c' arg \"%s\"\n", 1949 suboption, fragment_to_tempstr(&suboptionarg))); 1950 return 0; 1951 } 1952 continue; 1953 } 1954 1955 TRACE(("DATA_ERROR: skipping unknown token in shading control suboptionset (expecting option): \"%s\"\n", 1956 fragment_to_tempstr(&suboptionset))); 1957 pop_fragment(&suboptionset); 1958 } 1959 continue; 1960 } 1961 1962 if (extract_regis_extent(arg, &item)) { 1963 if (!load_regis_extent(fragment_to_tempstr(&item), 1964 ref_x, ref_y, 1965 &ref_x, &ref_y)) { 1966 TRACE(("DATA_ERROR: unable to parse extent in write shading option '%c': \"%s\"\n", 1967 option, fragment_to_tempstr(&item))); 1968 return 0; 1969 } 1970 TRACE(("shading reference = %d,%d (%s)\n", ref_x, ref_y, 1971 ((reference_dim == WRITE_SHADING_REF_X) 1972 ? "X" 1973 : "Y"))); 1974 continue; 1975 } 1976 1977 if (extract_regis_num(arg, &item)) { 1978 if (!regis_num_to_int(&item, &shading_enabled)) { 1979 TRACE(("DATA_ERROR: unable to parse int in write shading option '%c': \"%s\"\n", 1980 option, fragment_to_tempstr(&item))); 1981 return 0; 1982 } 1983 if (shading_enabled < 0 || shading_enabled > 1) { 1984 TRACE(("interpreting out of range value %d as 0 FIXME\n", shading_enabled)); 1985 shading_enabled = 0; 1986 } 1987 TRACE(("shading enabled = %d\n", shading_enabled)); 1988 continue; 1989 } 1990 1991 TRACE(("DATA_ERROR: skipping unknown token in shade suboption: \"%s\"\n", 1992 fragment_to_tempstr(arg))); 1993 pop_fragment(arg); 1994 } 1995 1996 if (shading_enabled) { 1997 out->shading_enabled = 1U; 1998 out->shading_reference_dim = reference_dim; 1999 out->shading_reference = ((reference_dim == WRITE_SHADING_REF_X) 2000 ? ref_x 2001 : ref_y); 2002 out->shading_character = shading_character; 2003 } else { 2004 /* FIXME: confirm there is no effect if shading isn't enabled 2005 * in the same command 2006 */ 2007 out->shading_enabled = 0U; 2008 } 2009 } 2010 break; 2011 case 'V': 2012 case 'v': 2013 TRACE(("write control switch to overlay writing mode \"%s\"\n", 2014 fragment_to_tempstr(arg))); 2015 out->write_style = WRITE_STYLE_OVERLAY; 2016 break; 2017 default: 2018 TRACE(("DATA_ERROR: ignoring unknown ReGIS write option \"%c\" arg \"%s\"\n", 2019 option, fragment_to_tempstr(arg))); 2020 return 0; 2021 } 2022 2023 return 1; 2024} 2025 2026static int 2027load_regis_write_control_set(RegisParseState *state, 2028 Graphic const *graphic, 2029 int cur_x, int cur_y, 2030 RegisDataFragment *controls, 2031 RegisWriteControls *out) 2032{ 2033 RegisDataFragment optionset; 2034 RegisDataFragment arg; 2035 char option; 2036 2037 while (controls->pos < controls->len) { 2038 skip_regis_whitespace(controls); 2039 2040 if (extract_regis_optionset(controls, &optionset)) { 2041 TRACE(("got regis write control optionset: \"%s\"\n", 2042 fragment_to_tempstr(&optionset))); 2043 while (optionset.pos < optionset.len) { 2044 skip_regis_whitespace(&optionset); 2045 if (peek_fragment(&optionset) == ',') { 2046 pop_fragment(&optionset); 2047 continue; 2048 } 2049 if (extract_regis_option(&optionset, &option, &arg)) { 2050 skip_regis_whitespace(&arg); 2051 TRACE(("got regis write control option and value: \"%c\" \"%s\"\n", 2052 option, fragment_to_tempstr(&arg))); 2053 if (!load_regis_write_control(state, graphic, 2054 cur_x, cur_y, 2055 option, &arg, out)) { 2056 return 0; 2057 } 2058 continue; 2059 } 2060 2061 TRACE(("DATA_ERROR: skipping unknown token in write control optionset (expecting option): \"%s\"\n", 2062 fragment_to_tempstr(&optionset))); 2063 pop_fragment(&optionset); 2064 } 2065 continue; 2066 } 2067 2068 TRACE(("DATA_ERROR: skipping unknown token in write controls (expecting optionset): \"%s\"\n", 2069 fragment_to_tempstr(controls))); 2070 pop_fragment(controls); 2071 } 2072 2073 return 1; 2074} 2075 2076static void 2077init_regis_write_controls(int terminal_id, unsigned all_planes, RegisWriteControls *controls) 2078{ 2079 controls->pv_multiplier = 1U; 2080 controls->pattern = 0xff; /* solid */ 2081 controls->pattern_multiplier = 2U; 2082 controls->invert_pattern = 0U; 2083 controls->plane_mask = all_planes; 2084 controls->write_style = WRITE_STYLE_OVERLAY; 2085 switch (terminal_id) { 2086 case 125: /* FIXME */ 2087 case 240: /* FIXME */ 2088 case 241: /* FIXME */ 2089 case 330: 2090 controls->foreground = 3U; 2091 break; 2092 case 340: 2093 controls->foreground = 7U; 2094 break; 2095 default: /* FIXME */ 2096 controls->foreground = 63U; 2097 break; 2098 } 2099 controls->shading_enabled = 0U; 2100 controls->shading_character = '\0'; 2101 controls->shading_reference = 0; /* no meaning if shading is disabled */ 2102 controls->shading_reference_dim = WRITE_SHADING_REF_Y; 2103 /* FIXME: add the rest */ 2104} 2105 2106static void 2107copy_regis_write_controls(RegisWriteControls const *src, 2108 RegisWriteControls *dst) 2109{ 2110 dst->pv_multiplier = src->pv_multiplier; 2111 dst->pattern = src->pattern; 2112 dst->pattern_multiplier = src->pattern_multiplier; 2113 dst->invert_pattern = src->invert_pattern; 2114 dst->foreground = src->foreground; 2115 dst->plane_mask = src->plane_mask; 2116 dst->write_style = src->write_style; 2117 dst->shading_enabled = src->shading_enabled; 2118 dst->shading_character = src->shading_character; 2119 dst->shading_reference = src->shading_reference; 2120 dst->shading_reference_dim = src->shading_reference_dim; 2121} 2122 2123static void 2124init_regis_graphics_context(int terminal_id, RegisGraphicsContext *context) 2125{ 2126 context->terminal_id = terminal_id; 2127 /* 2128 * Generate a mask covering all valid color register address bits 2129 * (but don't bother past 2**16). 2130 */ 2131 context->all_planes = (unsigned) context->graphic->valid_registers; 2132 context->all_planes--; 2133 context->all_planes |= 1U; 2134 context->all_planes |= context->all_planes >> 1U; 2135 context->all_planes |= context->all_planes >> 2U; 2136 context->all_planes |= context->all_planes >> 4U; 2137 context->all_planes |= context->all_planes >> 8U; 2138 2139 init_regis_write_controls(terminal_id, context->all_planes, &context->persistent_write_controls); 2140 copy_regis_write_controls(&context->persistent_write_controls, &context->temporary_write_controls); 2141 2142 /* FIXME: coordinates */ 2143 /* FIXME: scrolling */ 2144 /* FIXME: output maps */ 2145 context->background = 0U; 2146 /* FIXME: input cursor location */ 2147 /* FIXME: input cursor style */ 2148 context->graphics_output_cursor_x = 0; 2149 context->graphics_output_cursor_y = 0; 2150 /* FIXME: output cursor style */ 2151 /* FIXME: text settings */ 2152} 2153 2154static int 2155parse_regis_command(RegisParseState *state) 2156{ 2157 char ch = peek_fragment(&state->input); 2158 if (ch == '\0') 2159 return 0; 2160 2161 if (!extract_regis_command(&state->input, &ch)) 2162 return 0; 2163 2164 switch (ch) { 2165 case 'C': 2166 case 'c': 2167 /* Curve 2168 2169 * C 2170 * (A) # set the arc length in degrees (+ or nothing for 2171 * # counter-clockwise, - for clockwise, rounded to the 2172 * # closest integer degree) 2173 * (B) # begin closed curve sequence (must have at least two 2174 * # values; this option can not be nested) 2175 * (C) # position is the center, current location is the 2176 * # circumference (stays in effect until next command) 2177 * (E) # end curve sequence (drawing is performed here) 2178 * (S) # begin open curve sequence 2179 * (W) # temporary write options (see write command) 2180 * [<center, circumference position>] # center if (C), otherwise point on circumference 2181 * [<point in curve sequence>]... # if between (B) and (E) 2182 * <pv>... # if between (B) and (E) 2183 */ 2184 TRACE(("found ReGIS command \"%c\" (curve)\n", ch)); 2185 state->command = 'c'; 2186 state->curve_mode = CURVE_POSITION_ARC_EDGE; 2187 state->arclen = 360; 2188 state->num_points = 0U; 2189 break; 2190 case 'F': 2191 case 'f': 2192 /* Fill 2193 2194 * F 2195 * (V) # polygon (see vector command) 2196 * (C) # curve (see curve command) 2197 * (W) # temporary write options (see write command) 2198 */ 2199 TRACE(("found ReGIS command \"%c\" (filled polygon)\n", ch)); 2200 state->command = 'f'; 2201 break; 2202 case 'L': 2203 case 'l': 2204 /* Load 2205 2206 * L 2207 * (A) # set character set number and name 2208 * "ascii"xx,xx,xx,xx,xx,xx,xx,xx # pixel values 2209 */ 2210 TRACE(("found ReGIS command \"%c\" (load charset)\n", ch)); 2211 state->command = 'l'; 2212 break; 2213 case 'P': 2214 case 'p': 2215 /* Position 2216 2217 * P 2218 * (B) # begin bounded position stack (last point returns to first) 2219 * (E) # end position stack 2220 * (P) # select graphics page for the input and output cursors 2221 * (S) # begin unbounded position stack 2222 * (W) # temporary write options (see write command) 2223 * <pv> # move: 0 == right, 1 == upper right, ..., 7 == lower right 2224 * [<position>] # move to position (X, Y, or both) 2225 * 2226 * Note the stack does not need to be ended before the next command 2227 * Note: maximum depth is 16 levels 2228 */ 2229 TRACE(("found ReGIS command \"%c\" (position)\n", ch)); 2230 state->command = 'p'; 2231 break; 2232 case 'R': 2233 case 'r': 2234 /* Report 2235 2236 * R 2237 * (E) # parse error 2238 * (I<val>) # set input mode (0 == oneshot, 1 == multiple) (always returns CR) 2239 * (L) # character set 2240 * (M(<name>) # macrograph contents 2241 * (M(=) # macrograph storage 2242 * (P) # cursor position 2243 * (P(I)) # interactive cursor position 2244 */ 2245 TRACE(("found ReGIS command \"%c\" (report status)\n", ch)); 2246 state->command = 'r'; 2247 break; 2248 case 'S': 2249 case 's': 2250 /* Screen 2251 2252 * S 2253 * (A[<upper left>][<lower right>]) 2254 * (C<setting> # 0 (cursor output off), 1 (cursor output on) 2255 * (E # erase to background color, resets shades, curves, and stacks 2256 * (H(P<printer offset>)[<print area cornet>][<print area corner>) 2257 * (I<color register>) # set the background to a specific register 2258 * (I(<rgb>)) # set the background to the register closest to an RGB value 2259 * (I(<hls>)) # set the background to the register closest to an HLS color 2260 * (M<color index to set>(L<mono level>)...) # level is 0 ... 100 (sets grayscale registers only) 2261 * (M<color index to set>(<RGB code>)...) # codes are D (black), R (red), G (green), B (blue), C (cyan), Y (yellow), M (magenta), W (white) (sets color and grayscale registers) 2262 * (M<color index to set>(A<RGB code>)...) # codes are D (black), R (red), G (green), B (blue), C (cyan), Y (yellow), M (magenta), W (white) (sets color registers only) 2263 * (M<color index to set>(H<hue>L<lightness>S<saturation>)...) # 0..360, 0..100, 0..100 (sets color and grayscale registers) 2264 * (M<color index to set>(AH<hue>L<lightness>S<saturation>)...) # 0..360, 0..100, 0..100 (sets color registers only) 2265 * (P<graphics page number>) # 0 (default) or 1 2266 * (T(<time delay ticks>) # 60 ticks per second, up to 32767 ticks 2267 * (W(M<factor>) # PV value 2268 * [scroll offset] # optional 2269 */ 2270 TRACE(("found ReGIS command \"%c\" (screen)\n", ch)); 2271 state->command = 's'; 2272 break; 2273 case 'T': 2274 case 't': 2275 /* Text 2276 2277 * T 2278 * (A0L"<designator>")) # specify a built-in set for GL via two-char designator 2279 * (A0R"<designator>")) # specify a built-in set for GR via two-char or three-char designator 2280 * (A<num>R"<designator>")) # specify a user-loaded (1-3) set for GR via two-char or three-char designator 2281 * (B) # begin temporary text control 2282 * (D<angle>) # specify a string tilt 2283 * (E) # end temporary text control 2284 * (H<factor>) # select a height multiplier (1-256) 2285 * (I<angle>) # italics: no slant (0), lean back (-1 though -45), lean forward (+1 through +45) 2286 * (M[width factor,height factor]) # select size multipliers (width 1-16) (height 1-256) 2287 * (S<size id>) # select one of the 17 standard cell sizes 2288 * (S[dimensions]) # set a custom display cell size (char with border) 2289 * (U[dimensions]) # set a custom unit cell size (char size) 2290 * (W<write command>) # temporary write options (see write command) 2291 * [<char offset>] # optional offset between characters 2292 * <PV spacing> # for subscripts and superscripts 2293 * '<text>' # optional 2294 * "<text>" # optional 2295 */ 2296 TRACE(("found ReGIS command \"%c\" (text)\n", ch)); 2297 state->command = 't'; 2298 break; 2299 case 'V': 2300 case 'v': 2301 /* Vector 2302 2303 * V 2304 * (B) # begin bounded position stack (last point returns to first) 2305 * (E) # end position stack 2306 * (S) # begin unbounded position stack 2307 * (W) # temporary write options (see write command) 2308 * <pv> # draw a line to the pixel vector 2309 * [] # draw a dot at the current location 2310 * [<position>] # draw a line to position 2311 */ 2312 TRACE(("found ReGIS command \"%c\" (vector)\n", ch)); 2313 state->command = 'v'; 2314 break; 2315 case 'W': 2316 case 'w': 2317 /* Write 2318 2319 * W 2320 * (C) # complement writing mode 2321 * (E) # erase writing mode 2322 * (F<plane>) # set the foreground intensity to a specific register 2323 * (I<color register>) # set the foreground to a specific register 2324 * (I(<rgb>)) # set the foreground to the register closest to an RGB value 2325 * (I(<hls>)) # set the foreground to the register closest to an HLS color 2326 * (M<pixel vector multiplier>) # set the multiplication factor 2327 * (N<setting>) # 0 == negative patterns disabled, 1 == negative patterns enabled 2328 * (P<pattern number>) # 0..9: 0 == none, 1 == solid, 2 == 50% dash, 3 == dash-dot 2329 * (P<pattern bits>) # 2 to 8 bits represented as a 0/1 sequence 2330 * (P<(M<pattern multiplier>)) 2331 * (R) # replacement writing mode 2332 * (S'<character>') # set shading character 2333 * (S<setting>) # 0 == disable shding, 1 == enable shading 2334 * (S[reference point]) # set a horizontal reference line including this point 2335 * (S(X)[reference point]) # set a vertical reference line including this point 2336 * (V) # overlay writing mode 2337 */ 2338 TRACE(("found ReGIS command \"%c\" (write parameters)\n", ch)); 2339 state->command = 'w'; 2340 break; 2341 case '@': 2342 /* Macrograph */ 2343 TRACE(("found ReGIS macrograph command\n")); 2344 ch = pop_fragment(&state->input); 2345 TRACE(("inspecting macrograph character \"%c\"\n", ch)); 2346 switch (ch) { 2347 case '.': 2348 TRACE(("clearing all macrographs FIXME\n")); 2349 /* FIXME: handle */ 2350 break; 2351 case ':': 2352 TRACE(("defining macrograph FIXME\n")); 2353 /* FIXME: parse, handle :<name> */ 2354 break; 2355 case ';': 2356 TRACE(("DATA_ERROR: found extraneous terminator for macrograph definition\n")); 2357 break; 2358 default: 2359 if ((ch > 'A' && ch < 'Z') || (ch > 'a' && ch < 'z')) { 2360 TRACE(("expanding macrograph \"%c\" FIXME\n", ch)); 2361 /* FIXME: handle */ 2362 } else { 2363 TRACE(("DATA_ERROR: unknown macrograph subcommand \"%c\"\n", ch)); 2364 } 2365 /* FIXME: parse, handle */ 2366 break; 2367 } 2368 break; 2369 default: 2370 TRACE(("DATA_ERROR: unknown ReGIS command %04x (%c)\n", 2371 (int) ch, ch)); 2372 state->command = '_'; 2373 state->option = '_'; 2374 return 0; 2375 } 2376 2377 state->option = '_'; 2378 2379 return 1; 2380} 2381 2382static int 2383parse_regis_optionset(RegisParseState *state) 2384{ 2385 if (!extract_regis_optionset(&state->input, &state->optionset)) 2386 return 0; 2387 2388 TRACE(("found ReGIS optionset \"%s\"\n", fragment_to_tempstr(&state->optionset))); 2389 state->option = '_'; 2390 2391 return 1; 2392} 2393 2394static int 2395parse_regis_option(RegisParseState *state, RegisGraphicsContext *context) 2396{ 2397 RegisDataFragment optionarg; 2398 2399 if (!extract_regis_option(&state->optionset, &state->option, &optionarg)) 2400 return 0; 2401 skip_regis_whitespace(&optionarg); 2402 2403 TRACE(("found ReGIS option \"%c\": \"%s\"\n", 2404 state->option, fragment_to_tempstr(&optionarg))); 2405 2406 switch (state->command) { 2407 case 'c': 2408 TRACE(("inspecting curve option \"%c\" with value \"%s\"\n", 2409 state->option, fragment_to_tempstr(&optionarg))); 2410 switch (state->option) { 2411 case 'A': 2412 case 'a': 2413 TRACE(("found arc length \"%s\"\n", fragment_to_tempstr(&optionarg))); 2414 { 2415 RegisDataFragment arclen; 2416 2417 if (!extract_regis_num(&optionarg, &arclen)) { 2418 TRACE(("DATA_ERROR: expected int in curve arclen option: \"%s\"\n", 2419 fragment_to_tempstr(&optionarg))); 2420 break; 2421 } 2422 TRACE(("arc length string %s\n", fragment_to_tempstr(&arclen))); 2423 if (!regis_num_to_int(&arclen, &state->arclen)) { 2424 TRACE(("DATA_ERROR: unable to parse int in curve arclen option: \"%s\"\n", 2425 fragment_to_tempstr(&arclen))); 2426 break; 2427 } 2428 TRACE(("value of arc length is %d\n", state->arclen)); 2429 while (state->arclen < -360) 2430 state->arclen += 360; 2431 while (state->arclen > 360) 2432 state->arclen -= 360; 2433 TRACE(("using final arc length %d\n", state->arclen)); 2434 } 2435 break; 2436 case 'B': 2437 case 'b': 2438 TRACE(("begin closed curve \"%s\"\n", fragment_to_tempstr(&optionarg))); 2439 if (fragment_len(&optionarg)) { 2440 TRACE(("DATA_ERROR: invalid closed curve option \"%s\"\n", 2441 fragment_to_tempstr(&optionarg))); 2442 break; 2443 } 2444 state->curve_mode = CURVE_POSITION_CLOSED_CURVE; 2445 state->num_points = 0U; 2446 state->x_points[state->num_points] = context->graphics_output_cursor_x; 2447 state->y_points[state->num_points] = context->graphics_output_cursor_y; 2448 state->num_points++; 2449 break; 2450 case 'C': 2451 case 'c': 2452 TRACE(("found center position mode \"%s\"\n", 2453 fragment_to_tempstr(&optionarg))); 2454 if (fragment_len(&optionarg)) { 2455 TRACE(("DATA_ERROR: invalid center position option \"%s\"\n", 2456 fragment_to_tempstr(&optionarg))); 2457 break; 2458 } 2459 state->curve_mode = CURVE_POSITION_ARC_CENTER; 2460 break; 2461 case 'E': 2462 case 'e': 2463 TRACE(("end curve \"%s\"\n", fragment_to_tempstr(&optionarg))); 2464 switch (state->curve_mode) { 2465 case CURVE_POSITION_CLOSED_CURVE: 2466 { 2467 int i; 2468 2469#ifdef DEBUG_SPLINE_POINTS 2470 printf("points: \n"); 2471 for (i = 0; i < (int) state->num_points; i++) 2472 printf(" %d,%d\n", 2473 state->x_points[i], state->y_points[i]); 2474#endif 2475 2476#ifdef DEBUG_SPLINE_WITH_ROTATION 2477 { 2478 static int shift = 0; 2479 int temp_x[MAX_CURVE_POINTS], temp_y[MAX_CURVE_POINTS]; 2480 shift++; 2481 shift = shift % state->num_points; 2482 for (i = 0; i < (int) state->num_points; i++) { 2483 temp_x[i] = state->x_points[i]; 2484 temp_y[i] = state->y_points[i]; 2485 } 2486 for (i = 0; i < (int) state->num_points; i++) { 2487 state->x_points[i] = temp_x[(i + shift) % state->num_points]; 2488 state->y_points[i] = temp_y[(i + shift) % state->num_points]; 2489 } 2490 2491#ifdef DEBUG_SPLINE_POINTS 2492 printf("after shift %d: \n", shift); 2493 for (i = 0; i < (int) state->num_points; i++) 2494 printf(" %d,%d\n", 2495 state->x_points[i], state->y_points[i]); 2496#endif 2497 } 2498#endif 2499 2500 for (i = (int) state->num_points; i > 0; i--) { 2501 state->x_points[i] = state->x_points[i - 1]; 2502 state->y_points[i] = state->y_points[i - 1]; 2503 } 2504 state->x_points[0] = state->x_points[state->num_points]; 2505 state->y_points[0] = state->y_points[state->num_points]; 2506 state->num_points++; 2507 for (i = (int) state->num_points; i > 0; i--) { 2508 state->x_points[i] = state->x_points[i - 1]; 2509 state->y_points[i] = state->y_points[i - 1]; 2510 } 2511 state->x_points[0] = state->x_points[state->num_points - 1]; 2512 state->y_points[0] = state->y_points[state->num_points - 1]; 2513 state->num_points++; 2514 state->x_points[state->num_points] = state->x_points[2]; 2515 state->y_points[state->num_points] = state->y_points[2]; 2516 state->num_points++; 2517#ifdef DEBUG_SPLINE_WITH_OVERDRAW 2518 state->x_points[state->num_points] = state->x_points[3]; 2519 state->y_points[state->num_points] = state->y_points[3]; 2520 state->num_points++; 2521 state->x_points[state->num_points] = state->x_points[4]; 2522 state->y_points[state->num_points] = state->y_points[4]; 2523 state->num_points++; 2524#endif 2525#ifdef DEBUG_SPLINE_POINTS 2526 printf("after points added: \n"); 2527 for (i = 0; i < (int) state->num_points; i++) 2528 printf(" %d,%d\n", 2529 state->x_points[i], state->y_points[i]); 2530#endif 2531 } 2532 TRACE(("drawing closed spline\n")); 2533 global_context = context; /* FIXME: remove after updating spline code */ 2534 plotCubicSpline((int) state->num_points - 1, 2535 state->x_points, state->y_points, 2536 1); 2537 break; 2538 case CURVE_POSITION_OPEN_CURVE: 2539 TRACE(("drawing open spline\n")); 2540#ifdef DEBUG_SPLINE_POINTS 2541 { 2542 int i; 2543 2544 printf("points: \n"); 2545 for (i = 0; i < (int) state->num_points; i++) 2546 printf(" %d,%d\n", 2547 state->x_points[i], state->y_points[i]); 2548 } 2549#endif 2550 global_context = context; /* FIXME: remove after updating spline code */ 2551 plotCubicSpline((int) state->num_points - 1, 2552 state->x_points, state->y_points, 2553 1); 2554 break; 2555 default: 2556 TRACE(("DATA_ERROR: end curve option unexpected \"%s\"\n", 2557 fragment_to_tempstr(&optionarg))); 2558 break; 2559 } 2560 break; 2561 case 'S': 2562 case 's': 2563 TRACE(("begin open curve \"%s\"\n", fragment_to_tempstr(&optionarg))); 2564 if (fragment_len(&optionarg)) { 2565 TRACE(("DATA_ERROR: invalid open curve option \"%s\"\n", 2566 fragment_to_tempstr(&optionarg))); 2567 break; 2568 } 2569 state->curve_mode = CURVE_POSITION_OPEN_CURVE; 2570 state->num_points = 0U; 2571 state->x_points[state->num_points] = context->graphics_output_cursor_x; 2572 state->y_points[state->num_points] = context->graphics_output_cursor_y; 2573 state->num_points++; 2574 TRACE(("first point on curve with location %d,%d\n", 2575 context->graphics_output_cursor_x, 2576 context->graphics_output_cursor_y)); 2577 break; 2578 case 'W': 2579 case 'w': 2580 TRACE(("found temporary write options \"%s\"\n", 2581 fragment_to_tempstr(&optionarg))); 2582 if (!load_regis_write_control_set(state, context->graphic, 2583 context->graphics_output_cursor_x, context->graphics_output_cursor_y, 2584 &optionarg, &context->temporary_write_controls)) { 2585 TRACE(("DATA_ERROR: invalid temporary write options \"%s\"\n", 2586 fragment_to_tempstr(&optionarg))); 2587 break; 2588 } 2589 break; 2590 default: 2591 TRACE(("DATA_ERROR: ignoring unknown ReGIS curve command option '%c' arg \"%s\"\n", 2592 state->option, fragment_to_tempstr(&optionarg))); 2593 break; 2594 } 2595 break; 2596 case 'f': 2597 TRACE(("inspecting fill option \"%c\" with value \"%s\"\n", 2598 state->option, fragment_to_tempstr(&optionarg))); 2599 switch (state->option) { 2600 case 'C': 2601 case 'c': 2602 state->command = 'c'; 2603 state->option = '_'; 2604 break; 2605 case 'V': 2606 case 'v': 2607 state->command = 'v'; 2608 state->option = '_'; 2609 break; 2610 case 'W': 2611 case 'w': 2612 TRACE(("found temporary write options \"%s\"\n", 2613 fragment_to_tempstr(&optionarg))); 2614 if (!load_regis_write_control_set(state, context->graphic, 2615 context->graphics_output_cursor_x, context->graphics_output_cursor_y, 2616 &optionarg, &context->temporary_write_controls)) { 2617 TRACE(("DATA_ERROR: invalid temporary write options \"%s\"\n", 2618 fragment_to_tempstr(&optionarg))); 2619 break; 2620 } 2621 break; 2622 default: 2623 TRACE(("DATA_ERROR: ignoring unknown ReGIS fill command option '%c' arg \"%s\"\n", 2624 state->option, fragment_to_tempstr(&optionarg))); 2625 break; 2626 } 2627 break; 2628 case 'l': 2629 TRACE(("inspecting load option \"%c\" with value \"%s\"\n", 2630 state->option, fragment_to_tempstr(&optionarg))); 2631 /* FIXME: parse options */ 2632 switch (state->option) { 2633 case 'A': 2634 case 'a': 2635 TRACE(("found character specifier option \"%s\" FIXME\n", 2636 fragment_to_tempstr(&optionarg))); 2637 /* FIXME: handle */ 2638 break; 2639 default: 2640 TRACE(("DATA_ERROR: ignoring unknown ReGIS load command option '%c' arg \"%s\"\n", 2641 state->option, fragment_to_tempstr(&optionarg))); 2642 break; 2643 } 2644 break; 2645 case 'p': 2646 TRACE(("inspecting position option \"%c\" with value \"%s\"\n", 2647 state->option, fragment_to_tempstr(&optionarg))); 2648 switch (state->option) { 2649 case 'B': 2650 case 'b': 2651 TRACE(("found begin bounded position stack \"%s\" FIXME\n", 2652 fragment_to_tempstr(&optionarg))); 2653 /* FIXME: handle */ 2654 break; 2655 case 'E': 2656 case 'e': 2657 TRACE(("found end position stack \"%s\" FIXME\n", 2658 fragment_to_tempstr(&optionarg))); 2659 /* FIXME: handle */ 2660 break; 2661 case 'P': 2662 case 'p': 2663 TRACE(("found graphics page \"%s\" FIXME\n", 2664 fragment_to_tempstr(&optionarg))); 2665 /* FIXME: handle */ 2666 break; 2667 case 'S': 2668 case 's': 2669 TRACE(("found begin unbounded position stack \"%s\" FIXME\n", 2670 fragment_to_tempstr(&optionarg))); 2671 /* FIXME: handle */ 2672 break; 2673 case 'W': 2674 case 'w': 2675 TRACE(("found temporary write options \"%s\"\n", 2676 fragment_to_tempstr(&optionarg))); 2677 if (!load_regis_write_control_set(state, context->graphic, 2678 context->graphics_output_cursor_x, context->graphics_output_cursor_y, 2679 &optionarg, &context->temporary_write_controls)) { 2680 TRACE(("DATA_ERROR: invalid temporary write options \"%s\"\n", 2681 fragment_to_tempstr(&optionarg))); 2682 } 2683 break; 2684 default: 2685 TRACE(("DATA_ERROR: ignoring unknown ReGIS position command option '%c' arg \"%s\"\n", 2686 state->option, fragment_to_tempstr(&optionarg))); 2687 break; 2688 } 2689 break; 2690 case 'r': 2691 TRACE(("inspecting report option \"%c\" with value \"%s\"\n", 2692 state->option, fragment_to_tempstr(&optionarg))); 2693 switch (state->option) { 2694 case 'E': 2695 case 'e': 2696 TRACE(("found parse error report \"%s\" FIXME\n", 2697 fragment_to_tempstr(&optionarg))); 2698 /* FIXME: handle */ 2699 break; 2700 case 'I': 2701 case 'i': 2702 TRACE(("found set input mode \"%s\" FIXME\n", 2703 fragment_to_tempstr(&optionarg))); 2704 /* FIXME: handle */ 2705 break; 2706 case 'L': 2707 case 'l': 2708 TRACE(("found character set report \"%s\" FIXME\n", 2709 fragment_to_tempstr(&optionarg))); 2710 /* FIXME: handle */ 2711 break; 2712 case 'M': 2713 case 'm': 2714 TRACE(("found macrograph report \"%s\" FIXME\n", 2715 fragment_to_tempstr(&optionarg))); 2716 /* FIXME: handle */ 2717 break; 2718 case 'P': 2719 case 'p': 2720 TRACE(("found cursor position report \"%s\" FIXME\n", 2721 fragment_to_tempstr(&optionarg))); 2722 /* FIXME: handle */ 2723 break; 2724 default: 2725 TRACE(("DATA_ERROR: ignoring unknown ReGIS report command option '%c' arg \"%s\"\n", 2726 state->option, fragment_to_tempstr(&optionarg))); 2727 break; 2728 } 2729 break; 2730 case 's': 2731 TRACE(("inspecting screen option \"%c\" with value \"%s\"\n", 2732 state->option, fragment_to_tempstr(&optionarg))); 2733 switch (state->option) { 2734 case 'A': 2735 case 'a': 2736 TRACE(("found address definition \"%s\" FIXME\n", 2737 fragment_to_tempstr(&optionarg))); 2738 /* FIXME: handle */ 2739 if (!fragment_len(&optionarg)) { 2740 TRACE(("DATA_ERROR: ignoring malformed ReGIS screen address definition option value \"%s\"\n", 2741 fragment_to_tempstr(&optionarg))); 2742 return 0; 2743 } 2744 break; 2745 case 'C': 2746 case 'c': 2747 TRACE(("found cursor control \"%s\" FIXME\n", 2748 fragment_to_tempstr(&optionarg))); 2749 /* FIXME: handle */ 2750 if (!fragment_len(&optionarg)) { 2751 TRACE(("DATA_ERROR: ignoring malformed ReGIS screen cursor control option value \"%s\"\n", 2752 fragment_to_tempstr(&optionarg))); 2753 return 0; 2754 } 2755 break; 2756 case 'E': 2757 case 'e': 2758 TRACE(("found erase request \"%s\"\n", fragment_to_tempstr(&optionarg))); 2759 if (fragment_len(&optionarg)) { 2760 TRACE(("DATA_ERROR: ignoring unexpected argument to ReGIS screen erase option \"%s\"\n", 2761 fragment_to_tempstr(&optionarg))); 2762 return 0; 2763 } 2764 draw_solid_rectangle(context->graphic, 0, 0, 2765 context->graphic->actual_width - 1, 2766 context->graphic->actual_height - 1, 2767 context->background); 2768 break; 2769 case 'H': 2770 case 'h': 2771 TRACE(("found hardcopy control \"%s\" FIXME\n", 2772 fragment_to_tempstr(&optionarg))); 2773 /* FIXME: handle */ 2774 if (!fragment_len(&optionarg)) { 2775 TRACE(("DATA_ERROR: ignoring malformed ReGIS screen hardcopy control option value \"%s\"\n", 2776 fragment_to_tempstr(&optionarg))); 2777 return 0; 2778 } 2779 break; 2780 case 'I': 2781 case 'i': 2782 TRACE(("found screen background color index \"%s\"\n", 2783 fragment_to_tempstr(&optionarg))); 2784 if (!load_regis_colorspec(context->graphic, &optionarg, &context->background)) { 2785 TRACE(("DATA_ERROR: screen background color specifier not recognized: \"%s\"\n", 2786 fragment_to_tempstr(&optionarg))); 2787 return 0; 2788 } 2789 break; 2790 case 'M': 2791 case 'm': 2792 TRACE(("found screen color register mapping \"%s\"\n", 2793 fragment_to_tempstr(&optionarg))); 2794 { 2795 RegisDataFragment regnum; 2796 RegisDataFragment colorspec; 2797 char ch; 2798 2799 while (fragment_len(&optionarg)) { 2800 if (skip_regis_whitespace(&optionarg)) 2801 continue; 2802 if (extract_regis_num(&optionarg, ®num)) { 2803 int register_num; 2804 int color_only; 2805 short r, g, b; 2806 2807 if (!regis_num_to_int(®num, ®ister_num)) { 2808 TRACE(("DATA_ERROR: unable to parse int in screen color register mapping option: \"%s\"\n", 2809 fragment_to_tempstr(®num))); 2810 return 0; 2811 } 2812 if (register_num < 0 || 2813 register_num > (int) context->graphic->valid_registers) { 2814 TRACE(("interpreting out of range register number %d as 0 FIXME\n", register_num)); 2815 register_num = 0; 2816 } 2817 skip_regis_whitespace(&optionarg); 2818 if (!extract_regis_optionset(&optionarg, &colorspec)) { 2819 TRACE(("DATA_ERROR: expected to find optionset after register number: \"%s\"\n", 2820 fragment_to_tempstr(&optionarg))); 2821 return 0; 2822 } 2823 2824 switch (peek_fragment(&colorspec)) { 2825 case 'A': 2826 case 'a': 2827 pop_fragment(&colorspec); 2828 color_only = 1; 2829 break; 2830 default: 2831 color_only = 0; 2832 break; 2833 } 2834 2835 TRACE(("mapping register %d to color spec: \"%s\"\n", 2836 register_num, fragment_to_tempstr(&colorspec))); 2837 if (fragment_len(&colorspec) == 1) { 2838 short l; 2839 ch = pop_fragment(&colorspec); 2840 2841 TRACE(("got regis RGB colorspec pattern: \"%s\"\n", 2842 fragment_to_tempstr(&colorspec))); 2843 switch (ch) { 2844 case 'D': 2845 case 'd': 2846 r = 0; 2847 g = 0; 2848 b = 0; 2849 l = 0; 2850 break; 2851 case 'R': 2852 case 'r': 2853 r = 100; 2854 g = 0; 2855 b = 0; 2856 l = 46; 2857 break; 2858 case 'G': 2859 case 'g': 2860 r = 0; 2861 g = 100; 2862 b = 0; 2863 l = 50; 2864 break; 2865 case 'B': 2866 case 'b': 2867 r = 0; 2868 g = 0; 2869 b = 100; 2870 l = 50; 2871 break; 2872 case 'C': 2873 case 'c': 2874 r = 0; 2875 g = 100; 2876 b = 100; 2877 l = 50; 2878 break; 2879 case 'Y': 2880 case 'y': 2881 r = 100; 2882 g = 100; 2883 b = 0; 2884 l = 50; 2885 break; 2886 case 'M': 2887 case 'm': 2888 r = 100; 2889 g = 0; 2890 b = 100; 2891 l = 50; 2892 break; 2893 case 'W': 2894 case 'w': 2895 r = 100; 2896 g = 100; 2897 b = 100; 2898 l = 100; 2899 break; 2900 default: 2901 TRACE(("unknown RGB color name: \"%c\"\n", ch)); 2902 return 0; 2903 } 2904 if (context->terminal_id == 240 || 2905 context->terminal_id == 330) { 2906 /* The VT240 and VT330 models force saturation to zero. */ 2907 hls2rgb(0, l, 0, &r, &g, &b); 2908 } 2909 } else { 2910 short h, l, s; 2911 2912 if (sscanf(fragment_to_tempstr(&colorspec), 2913 "%*1[Hh]%hd%*1[Ll]%hd%*1[Ss]%hd", 2914 &h, &l, &s) != 3) { 2915 h = 0; 2916 s = 0; 2917 if (sscanf(fragment_to_tempstr(&colorspec), 2918 "%*1[Ll]%hd", &l) != 1) { 2919 TRACE(("unrecognized colorspec: \"%s\"\n", 2920 fragment_to_tempstr(&colorspec))); 2921 return 0; 2922 } 2923 } 2924 if (context->terminal_id == 240 || 2925 context->terminal_id == 330) { 2926 /* The VT240 and VT330 models force saturation to zero. */ 2927 h = 0; 2928 s = 0; 2929 } 2930 hls2rgb(h, l, s, &r, &g, &b); 2931 } 2932 2933 if (color_only && 2934 (context->terminal_id == 240 || 2935 context->terminal_id == 330)) 2936 continue; 2937 update_color_register(context->graphic, 2938 (RegisterNum) register_num, 2939 r, g, b); 2940 continue; 2941 } 2942 2943 TRACE(("DATA_ERROR: ignoring unexpected character in ReGIS screen color register mapping value \"%c\"\n", 2944 pop_fragment(&optionarg))); 2945 return 0; 2946 } 2947 } 2948 break; 2949 case 'P': 2950 case 'p': 2951 TRACE(("found graphics page request \"%s\" FIXME\n", 2952 fragment_to_tempstr(&optionarg))); 2953 /* FIXME: handle */ 2954 if (!fragment_len(&optionarg)) { 2955 TRACE(("DATA_ERROR: ignoring malformed ReGIS screen graphics page option value \"%s\"\n", 2956 fragment_to_tempstr(&optionarg))); 2957 return 0; 2958 } 2959 break; 2960 case 'T': 2961 case 't': 2962 TRACE(("found time delay \"%s\" FIXME\n", fragment_to_tempstr(&optionarg))); 2963 /* FIXME: handle */ 2964 if (!fragment_len(&optionarg)) { 2965 TRACE(("DATA_ERROR: ignoring malformed ReGIS screen time delay option value \"%s\"\n", 2966 fragment_to_tempstr(&optionarg))); 2967 return 0; 2968 } 2969 break; 2970 case 'W': 2971 case 'w': 2972 TRACE(("found PV \"%s\" FIXME\n", fragment_to_tempstr(&optionarg))); 2973 /* FIXME: handle */ 2974 if (!fragment_len(&optionarg)) { 2975 TRACE(("DATA_ERROR: ignoring malformed ReGIS screen PV option value \"%s\"\n", 2976 fragment_to_tempstr(&optionarg))); 2977 return 0; 2978 } 2979 break; 2980 default: 2981 TRACE(("DATA_ERROR: ignoring unknown ReGIS screen command option '%c' arg \"%s\"\n", 2982 state->option, fragment_to_tempstr(&optionarg))); 2983 break; 2984 } 2985 break; 2986 case 't': 2987 TRACE(("inspecting text option \"%c\" with value \"%s\"\n", 2988 state->option, fragment_to_tempstr(&optionarg))); 2989 if (!fragment_len(&optionarg)) { 2990 TRACE(("DATA_ERROR: ignoring malformed ReGIS text command option value \"%s\"\n", 2991 fragment_to_tempstr(&optionarg))); 2992 return 0; 2993 } 2994 switch (state->option) { 2995 case 'A': 2996 case 'a': 2997 TRACE(("found character set specifier \"%s\" FIXME\n", 2998 fragment_to_tempstr(&optionarg))); 2999 /* FIXME: handle */ 3000 break; 3001 case 'B': 3002 case 'b': 3003 TRACE(("found beginning of temporary text control \"%s\" FIXME\n", 3004 fragment_to_tempstr(&optionarg))); 3005 /* FIXME: handle */ 3006 break; 3007 case 'D': 3008 case 'd': 3009 TRACE(("found string tilt control \"%s\" FIXME\n", 3010 fragment_to_tempstr(&optionarg))); 3011 /* FIXME: handle */ 3012 break; 3013 case 'E': 3014 case 'e': 3015 TRACE(("found end of temporary text control \"%s\" FIXME\n", 3016 fragment_to_tempstr(&optionarg))); 3017 /* FIXME: handle */ 3018 break; 3019 case 'H': 3020 case 'h': 3021 TRACE(("found height multiplier \"%s\" FIXME\n", 3022 fragment_to_tempstr(&optionarg))); 3023 /* FIXME: handle */ 3024 break; 3025 case 'I': 3026 case 'i': 3027 TRACE(("found italic control \"%s\" FIXME\n", 3028 fragment_to_tempstr(&optionarg))); 3029 /* FIXME: handle */ 3030 break; 3031 case 'M': 3032 case 'm': 3033 TRACE(("found size multiplier \"%s\" FIXME\n", 3034 fragment_to_tempstr(&optionarg))); 3035 /* FIXME: handle */ 3036 break; 3037 case 'S': 3038 case 's': 3039 TRACE(("found custom display cell size \"%s\" FIXME\n", 3040 fragment_to_tempstr(&optionarg))); 3041 /* FIXME: handle */ 3042 break; 3043 case 'U': 3044 case 'u': 3045 TRACE(("found custom display unit size \"%s\" FIXME\n", 3046 fragment_to_tempstr(&optionarg))); 3047 /* FIXME: handle */ 3048 break; 3049 default: 3050 TRACE(("DATA_ERROR: ignoring unknown ReGIS text command option '%c' arg \"%s\"\n", 3051 state->option, fragment_to_tempstr(&optionarg))); 3052 break; 3053 } 3054 break; 3055 case 'v': 3056 TRACE(("inspecting vector option \"%c\" with value \"%s\"\n", 3057 state->option, fragment_to_tempstr(&optionarg))); 3058 switch (state->option) { 3059 case 'B': 3060 case 'b': 3061 TRACE(("found begin bounded position stack \"%s\" FIXME\n", 3062 fragment_to_tempstr(&optionarg))); 3063 /* FIXME: handle */ 3064 break; 3065 case 'E': 3066 case 'e': 3067 TRACE(("found end position stack \"%s\" FIXME\n", 3068 fragment_to_tempstr(&optionarg))); 3069 /* FIXME: handle */ 3070 break; 3071 case 'S': 3072 case 's': 3073 TRACE(("found begin unbounded position stack \"%s\" FIXME\n", 3074 fragment_to_tempstr(&optionarg))); 3075 /* FIXME: handle */ 3076 break; 3077 case 'W': 3078 case 'w': 3079 TRACE(("found temporary write options \"%s\"\n", 3080 fragment_to_tempstr(&optionarg))); 3081 if (!load_regis_write_control_set(state, context->graphic, 3082 context->graphics_output_cursor_x, context->graphics_output_cursor_y, 3083 &optionarg, &context->temporary_write_controls)) { 3084 TRACE(("DATA_ERROR: invalid temporary write options \"%s\"\n", 3085 fragment_to_tempstr(&optionarg))); 3086 } 3087 break; 3088 default: 3089 TRACE(("DATA_ERROR: ignoring unknown ReGIS vector command option '%c' arg \"%s\"\n", 3090 state->option, fragment_to_tempstr(&optionarg))); 3091 break; 3092 } 3093 break; 3094 case 'w': 3095 skip_regis_whitespace(&optionarg); 3096 TRACE(("inspecting write option \"%c\" with value \"%s\"\n", 3097 state->option, fragment_to_tempstr(&optionarg))); 3098 if (!load_regis_write_control(state, context->graphic, 3099 context->graphics_output_cursor_x, context->graphics_output_cursor_y, 3100 state->option, &optionarg, &context->persistent_write_controls)) { 3101 TRACE(("DATA_ERROR: invalid write options\n")); 3102 } 3103 break; 3104 default: 3105 TRACE(("DATA_ERROR: unexpected option in \"%c\" command: \"%s\"\n", 3106 state->command, fragment_to_tempstr(&optionarg))); 3107 return 0; 3108 } 3109 3110 return 1; 3111} 3112 3113static int 3114parse_regis_items(RegisParseState *state, RegisGraphicsContext *context) 3115{ 3116 RegisDataFragment *input; 3117 RegisDataFragment item; 3118 3119 switch (state->level) { 3120 case INPUT: 3121 input = &state->input; 3122 break; 3123 case OPTIONSET: 3124 input = &state->optionset; 3125 break; 3126 default: 3127 TRACE(("invalid parse level: %d\n", state->level)); 3128 return 0; 3129 } 3130 3131 if (input->pos >= input->len) 3132 return 0; 3133 3134 if (extract_regis_extent(input, &item)) { 3135 TRACE(("found extent \"%s\"\n", fragment_to_tempstr(&item))); 3136 switch (state->command) { 3137 case 'c': 3138 { 3139 int orig_x, orig_y; 3140 int new_x, new_y; 3141 3142 if (state->num_points > 0) { 3143 orig_x = state->x_points[state->num_points - 1]; 3144 orig_y = state->y_points[state->num_points - 1]; 3145 } else { 3146 orig_x = context->graphics_output_cursor_x; 3147 orig_y = context->graphics_output_cursor_y; 3148 } 3149 if (!load_regis_extent(fragment_to_tempstr(&item), 3150 orig_x, orig_y, 3151 &new_x, &new_y)) { 3152 TRACE(("DATA_ERROR: unable to parse extent in '%c' command: \"%s\"\n", 3153 state->command, fragment_to_tempstr(&item))); 3154 break; 3155 } 3156 3157 switch (state->curve_mode) { 3158 case CURVE_POSITION_ARC_CENTER: 3159 case CURVE_POSITION_ARC_EDGE: 3160 { 3161 double radians; 3162 int degrees; 3163 int c_x, c_y; 3164 int e_x, e_y; 3165 int e_x_final = 0, e_y_final = 0; 3166 3167 if (state->curve_mode == CURVE_POSITION_ARC_CENTER) { 3168 c_x = new_x; 3169 c_y = new_y; 3170 e_x = orig_x; 3171 e_y = orig_y; 3172 } else { 3173 c_x = orig_x; 3174 c_y = orig_y; 3175 e_x = new_x; 3176 e_y = new_y; 3177 } 3178 3179 radians = atan2((double) (c_y - e_y), 3180 (double) (e_x - c_x)); 3181 degrees = (int) (360.0 * radians / (2.0 * M_PI)); 3182 if (degrees < 0) 3183 degrees += 360; 3184 3185 TRACE(("drawing arc centered at location %d,%d to location %d,%d from %d degrees (%g radians) for %d degrees\n", 3186 c_x, c_y, 3187 e_x, e_y, 3188 degrees, radians, state->arclen)); 3189 draw_patterned_arc(context, 3190 c_x, c_y, 3191 e_x, e_y, 3192 degrees, state->arclen, 3193 &e_x_final, &e_y_final); 3194 3195#ifdef DEBUG_ARC_CENTER 3196 draw_solid_pixel(context->graphic, c_x + 1, c_y, 3U); 3197 draw_solid_pixel(context->graphic, c_x - 1, c_y, 3U); 3198 draw_solid_pixel(context->graphic, c_x, c_y + 1, 3U); 3199 draw_solid_pixel(context->graphic, c_x, c_y - 1, 3U); 3200 draw_solid_pixel(context->graphic, c_x, c_y, 3U); 3201#endif 3202 3203#ifdef DEBUG_ARC_START 3204 draw_solid_pixel(context->graphic, e_x + 1, e_y, 2U); 3205 draw_solid_pixel(context->graphic, e_x - 1, e_y, 2U); 3206 draw_solid_pixel(context->graphic, e_x, e_y + 1, 2U); 3207 draw_solid_pixel(context->graphic, e_x, e_y - 1, 2U); 3208 draw_solid_pixel(context->graphic, e_x, e_y, 2U); 3209#endif 3210 3211#ifdef DEBUG_ARC_END 3212 draw_solid_pixel(context->graphic, e_x_final + 1, 3213 e_y_final + 1, 1U); 3214 draw_solid_pixel(context->graphic, e_x_final + 1, 3215 e_y_final - 1, 1U); 3216 draw_solid_pixel(context->graphic, e_x_final - 1, 3217 e_y_final + 1, 1U); 3218 draw_solid_pixel(context->graphic, e_x_final - 1, 3219 e_y_final - 1, 1U); 3220 draw_solid_pixel(context->graphic, e_x_final, 3221 e_y_final, 1U); 3222#endif 3223 3224 if (state->curve_mode == CURVE_POSITION_ARC_CENTER) { 3225 TRACE(("moving cursor to final point on arc %d,%d\n", 3226 e_x_final, e_y_final)); 3227 if (state->num_points > 0) { 3228 state->x_points[state->num_points - 1] = e_x_final; 3229 state->y_points[state->num_points - 1] = e_y_final; 3230 } 3231 context->graphics_output_cursor_x = e_x_final; 3232 context->graphics_output_cursor_y = e_y_final; 3233 } 3234 } 3235 break; 3236 case CURVE_POSITION_OPEN_CURVE: 3237 case CURVE_POSITION_CLOSED_CURVE: 3238 if (state->num_points >= MAX_INPUT_CURVE_POINTS) { 3239 TRACE(("DATA_ERROR: got curve point, but already have max points (%d)\n", state->num_points)); 3240 break; 3241 } 3242 state->x_points[state->num_points] = new_x; 3243 state->y_points[state->num_points] = new_y; 3244 state->num_points++; 3245 TRACE(("adding point to curve with location %d,%d\n", 3246 new_x, new_y)); 3247 break; 3248 default: 3249 TRACE(("ERROR: got position, but curve mode %d is unknown\n", state->curve_mode)); 3250 break; 3251 } 3252 } 3253 break; 3254 case 'p': 3255 /* FIXME TRACE(("DATA_ERROR: ignoring pen command with no location\n")); */ 3256 if (!load_regis_extent(fragment_to_tempstr(&item), 3257 context->graphics_output_cursor_x, context->graphics_output_cursor_y, 3258 &context->graphics_output_cursor_x, &context->graphics_output_cursor_y)) { 3259 TRACE(("DATA_ERROR: unable to parse extent in '%c' command: \"%s\"\n", 3260 state->command, fragment_to_tempstr(&item))); 3261 break; 3262 } 3263 TRACE(("moving pen to location %d,%d\n", 3264 context->graphics_output_cursor_x, 3265 context->graphics_output_cursor_y)); 3266 break; 3267 case 's': 3268 /* FIXME: parse, handle */ 3269 TRACE(("extent in screen command FIXME\n")); 3270 break; 3271 case 't': 3272 /* FIXME: parse, handle */ 3273 TRACE(("extent in text command FIXME\n")); 3274 break; 3275 case 'v': 3276 { 3277 int orig_x, orig_y; 3278 3279 orig_x = context->graphics_output_cursor_x; 3280 orig_y = context->graphics_output_cursor_y; 3281 if (!load_regis_extent(fragment_to_tempstr(&item), 3282 orig_x, orig_y, 3283 &context->graphics_output_cursor_x, &context->graphics_output_cursor_y)) { 3284 TRACE(("DATA_ERROR: unable to parse extent in '%c' command: \"%s\"\n", 3285 state->command, fragment_to_tempstr(&item))); 3286 break; 3287 } 3288 TRACE(("drawing line to location %d,%d\n", 3289 context->graphics_output_cursor_x, 3290 context->graphics_output_cursor_y)); 3291 draw_patterned_line(context, 3292 orig_x, orig_y, 3293 context->graphics_output_cursor_x, 3294 context->graphics_output_cursor_y); 3295 } 3296 break; 3297 default: 3298 TRACE(("DATA_ERROR: unexpected extent in \"%c\" command: \"%s\"\n", 3299 state->command, fragment_to_tempstr(&item))); 3300 break; 3301 } 3302 return 1; 3303 } 3304 3305 if (extract_regis_pixelvector(input, &item)) { 3306 TRACE(("found pixel vector \"%s\"\n", fragment_to_tempstr(&item))); 3307 switch (state->command) { 3308 case 'c': 3309 /* FIXME: parse, handle */ 3310 TRACE(("pixelvector in curve command FIXME\n")); 3311 break; 3312 /* FIXME: not sure if 'f' supports pvs */ 3313 case 'p': 3314 /* FIXME: error checking */ 3315 if (!load_regis_pixelvector(fragment_to_tempstr(&item), context->temporary_write_controls.pv_multiplier, 3316 context->graphics_output_cursor_x, context->graphics_output_cursor_y, 3317 &context->graphics_output_cursor_x, &context->graphics_output_cursor_y)) { 3318 TRACE(("DATA_ERROR: unable to parse pixel vector in '%c' command: \"%s\"\n", 3319 state->command, fragment_to_tempstr(&item))); 3320 break; 3321 } 3322 TRACE(("moving pen to location %d,%d\n", 3323 context->graphics_output_cursor_x, 3324 context->graphics_output_cursor_y)); 3325 break; 3326 case 's': 3327 /* FIXME: parse, handle scroll argument */ 3328 TRACE(("pixelvector in screen command FIXME\n")); 3329 break; 3330 case 't': 3331 /* FIXME: parse, handle */ 3332 TRACE(("pixelvector in text command FIXME\n")); 3333 break; 3334 case 'v': 3335 /* FIXME: error checking */ 3336 { 3337 int orig_x, orig_y; 3338 3339 orig_x = context->graphics_output_cursor_x; 3340 orig_y = context->graphics_output_cursor_y; 3341 if (!load_regis_pixelvector(fragment_to_tempstr(&item), context->temporary_write_controls.pv_multiplier, 3342 orig_x, orig_y, 3343 &context->graphics_output_cursor_x, &context->graphics_output_cursor_y)) { 3344 TRACE(("DATA_ERROR: unable to parse pixel vector in '%c' command: \"%s\"\n", 3345 state->command, fragment_to_tempstr(&item))); 3346 break; 3347 } 3348 TRACE(("drawing line to location %d,%d\n", 3349 context->graphics_output_cursor_x, 3350 context->graphics_output_cursor_y)); 3351 draw_patterned_line(context, orig_x, orig_y, 3352 context->graphics_output_cursor_x, 3353 context->graphics_output_cursor_y); 3354 } 3355 break; 3356 default: 3357 TRACE(("DATA_ERROR: unexpected pixel vector in \"%c\" command: \"%s\"\n", 3358 state->command, fragment_to_tempstr(&item))); 3359 break; 3360 } 3361 return 1; 3362 } 3363 3364 if (extract_regis_string(input, state->temp, state->templen)) { 3365 switch (state->command) { 3366 case 'l': 3367 TRACE(("found character to load: \"%s\" FIXME\n", state->temp)); 3368 /* FIXME: handle */ 3369 case 't': 3370 TRACE(("found string to draw: \"%s\" FIXME\n", state->temp)); 3371 /* FIXME: handle */ 3372 break; 3373 default: 3374 TRACE(("DATA_ERROR: unexpected string in \"%c\" command: \"%s\"\n", 3375 state->command, state->temp)); 3376 break; 3377 } 3378 return 1; 3379 } 3380 3381 /* hex values */ 3382 if (state->command == 'l') { 3383 char ch1 = peek_fragment(input); 3384 char ch2 = peek_fragment(input); 3385 if ((ch1 == '0' || 3386 ch1 == '1' || 3387 ch1 == '2' || 3388 ch1 == '3' || 3389 ch1 == '4' || 3390 ch1 == '5' || 3391 ch1 == '6' || 3392 ch1 == '7' || 3393 ch1 == '8' || 3394 ch1 == '9' || 3395 ch1 == 'a' || 3396 ch1 == 'b' || 3397 ch1 == 'c' || 3398 ch1 == 'd' || 3399 ch1 == 'e' || 3400 ch1 == 'f' || 3401 ch1 == 'A' || 3402 ch1 == 'B' || 3403 ch1 == 'C' || 3404 ch1 == 'D' || 3405 ch1 == 'E' || 3406 ch1 == 'F') && 3407 (ch2 == '0' || 3408 ch2 == '1' || 3409 ch2 == '2' || 3410 ch2 == '3' || 3411 ch2 == '4' || 3412 ch2 == '5' || 3413 ch2 == '6' || 3414 ch2 == '7' || 3415 ch2 == '8' || 3416 ch2 == '9' || 3417 ch2 == 'a' || 3418 ch2 == 'b' || 3419 ch2 == 'c' || 3420 ch2 == 'd' || 3421 ch2 == 'e' || 3422 ch2 == 'f' || 3423 ch2 == 'A' || 3424 ch2 == 'B' || 3425 ch2 == 'C' || 3426 ch2 == 'D' || 3427 ch2 == 'E' || 3428 ch2 == 'F')) { 3429 /* FIXME: handle */ 3430 TRACE(("found hex number: \"%c%c\" FIXME\n", ch1, ch2)); 3431 pop_fragment(input); 3432 pop_fragment(input); 3433 if (peek_fragment(input) == ',') 3434 pop_fragment(input); 3435 return 1; 3436 } 3437 } 3438 3439 return 0; 3440} 3441 3442/* 3443 * context: 3444 * two pages of 800x480 3445 * current page # 3446 * current command 3447 * persistent write options 3448 * temporary write options 3449 * output position stack 3450 */ 3451void 3452parse_regis(XtermWidget xw, ANSI *params, char const *string) 3453{ 3454 TScreen *screen = TScreenOf(xw); 3455 RegisGraphicsContext context; 3456 RegisParseState state; 3457 unsigned iterations; 3458 int charrow = 0; 3459 int charcol = 0; 3460 unsigned type = 1; /* FIXME: use page number */ 3461 3462 (void) xw; 3463 (void) string; 3464 (void) params; 3465 3466 TRACE(("ReGIS vector graphics mode, params=%d\n", params->a_nparam)); 3467 3468 init_fragment(&state.input, string); 3469 init_fragment(&state.optionset, ""); 3470 state.level = INPUT; 3471 state.templen = (unsigned) strlen(string) + 1U; 3472 if (!(state.temp = malloc((size_t) state.templen))) { 3473 TRACE(("Unable to allocate temporary buffer of size %u\n", state.templen)); 3474 return; 3475 } 3476 state.command = '_'; 3477 state.option = '_'; 3478 3479 memset(&context, 0, sizeof(context)); 3480 3481 context.graphic = get_new_or_matching_graphic(xw, 3482 charrow, charcol, 3483 800, 480, 3484 type); 3485 init_regis_graphics_context(screen->terminal_id, &context); 3486 context.graphic->valid = 1; 3487 context.graphic->dirty = 1; 3488 refresh_modified_displayed_graphics(screen); 3489 3490 iterations = 0U; 3491 for (;;) { 3492 state.level = INPUT; 3493 TRACE(("parsing at top level: %d of %d (next char %c)\n", 3494 state.input.pos, 3495 state.input.len, 3496 peek_fragment(&state.input))); 3497 if (skip_regis_whitespace(&state.input)) 3498 continue; 3499 iterations++; 3500 if (parse_regis_command(&state)) { 3501 if (iterations > ITERATIONS_BEFORE_REFRESH) { 3502 iterations = 0U; 3503 refresh_modified_displayed_graphics(screen); 3504 } 3505 context.graphic->dirty = 1; 3506 /* FIXME: verify that these are the things reset on a new command */ 3507 copy_regis_write_controls(&context.persistent_write_controls, &context.temporary_write_controls); 3508 context.pattern_count = 0U; 3509 context.pattern_bit = 1U; 3510 continue; 3511 } 3512 if (parse_regis_optionset(&state)) { 3513 state.level = OPTIONSET; 3514 TRACE(("parsing at optionset level: %d of %d\n", 3515 state.optionset.pos, 3516 state.optionset.len)); 3517 for (;;) { 3518 if (state.optionset.pos >= state.optionset.len) 3519 break; 3520 TRACE(("looking at optionset character: \"%c\"\n", 3521 peek_fragment(&state.optionset))); 3522 if (skip_regis_whitespace(&state.optionset)) 3523 continue; 3524 if (peek_fragment(&state.optionset) == ',') { 3525 pop_fragment(&state.optionset); 3526 continue; 3527 } 3528 if (parse_regis_option(&state, &context)) 3529 continue; 3530 if (parse_regis_items(&state, &context)) 3531 continue; 3532 if (state.optionset.pos >= state.optionset.len) 3533 break; 3534 TRACE(("DATA_ERROR: skipping unknown token in optionset: \"%c\"\n", 3535 pop_fragment(&state.optionset))); 3536 /* FIXME: suboptions */ 3537 } 3538 state.option = '_'; 3539 continue; 3540 } 3541 if (parse_regis_items(&state, &context)) 3542 continue; 3543 if (state.optionset.pos >= state.optionset.len) 3544 break; 3545 TRACE(("DATA_ERROR: skipping unknown token at top level: \"%c\"\n", 3546 pop_fragment(&state.input))); 3547 } 3548 3549 free(state.temp); 3550 3551 refresh_modified_displayed_graphics(screen); 3552 TRACE(("DONE! Successfully parsed ReGIS data.\n")); 3553} 3554