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