graphics_regis.c revision ad37e533
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* */
88typedef struct RegisPoint {
89    int  x, y;
90} RegisPoint;
91
92typedef 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
107typedef 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
136typedef 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
146typedef 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
167typedef 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
199typedef 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
229static RegisGraphicsContext persistent_context;
230static 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
273static 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);
277static 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
283static void
284init_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
295static void
296init_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
305static int
306ifloor(double d)
307{
308    double dl = floor(d);
309    return (int) dl;
310}
311
312static int
313isqrt(double d)
314{
315    double dl = sqrt(d);
316    return (int) dl;
317}
318
319static void
320draw_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
397static void
398shade_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
428static void
429shade_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
471static void
472draw_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
486static void
487shade_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
507static void
508draw_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
538static int
539sort_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
555static void
556draw_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
629static void
630draw_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
689static void
690draw_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
751typedef struct {
752    int dxx;
753    int dxy;
754    int dyx;
755    int dyy;
756} quadmap_coords;
757
758static void
759draw_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 */
922static RegisGraphicsContext *global_context;
923static void
924setPixel(int x, int y)
925{
926    draw_or_save_patterned_pixel(global_context, x, y);
927}
928
929static void
930plotLine(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
955static void
956plotQuadBezierSeg(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
1019static void
1020plotQuadBezier(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
1065static void
1066plotCubicBezierSeg(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
1188static void
1189plotCubicBezier(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
1289static void
1290plotQuadSpline(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
1366static void
1367plotCubicSpline(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
1479static unsigned
1480find_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
1542static void
1543dump_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)
1562static int
1563copy_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
1662static void
1663get_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 */
1832static XftFont *
1833find_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
2089static int
2090get_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
2152static unsigned
2153find_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
2229static int
2230get_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 */
2311static void
2312get_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
2420static unsigned
2421get_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
2441static void
2442draw_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
2517static void
2518move_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
2565static void
2566draw_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 */
2778static int
2779get_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
2879static void
2880init_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
2890static void
2891copy_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
2901static char
2902peek_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
2912static char
2913pop_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
2923static char
2924get_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
2936static unsigned
2937fragment_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
2946static int
2947fragment_consumed(RegisDataFragment const *fragment)
2948{
2949    assert(fragment);
2950
2951    return fragment->pos >= fragment->len;
2952}
2953
2954static void
2955fragment_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
2977static char const *
2978fragment_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
2988static int
2989skip_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
3012static int
3013extract_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
3049static int
3050extract_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
3096static int
3097extract_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
3130static int
3131extract_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
3154static int
3155extract_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
3219static int
3220extract_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
3285static int
3286extract_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
3401static int
3402regis_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
3424static int
3425load_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
3656static int
3657load_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
3727static int
3728to_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
3764static int
3765load_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
3843static int
3844load_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
3864static int
3865load_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
3889static int
3890load_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
3929static int
3930load_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
3971static int
3972load_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
3995static int
3996load_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
4032static int
4033load_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
4066static int
4067load_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
4514static int
4515load_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
4561static void
4562init_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
4594static void
4595map_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
4655static void
4656copy_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
4673static void
4674init_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
4690static void
4691copy_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
4707static void
4708init_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
4724static void
4725init_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
4779static int
4780parse_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
5008static int
5009parse_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
6838static int
6839expand_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
6861static int
6862parse_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
7431static int
7432parse_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
7584void
7585parse_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