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, &regnum)) {
6061 			int register_num;
6062 			int color_only;
6063 			short r, g, b;
6064 
6065 			if (!regis_num_to_int(&regnum, &register_num)) {
6066 			    TRACE(("DATA_ERROR: unable to parse int in screen color register mapping option: \"%s\"\n",
6067 				   fragment_to_tempstr(&regnum)));
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