"Fossies" - the Fresh Open Source Software Archive

Member "xterm-379/graphics_regis.c" (16 May 2022, 224306 Bytes) of package /linux/misc/xterm-379.tgz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "graphics_regis.c" see the Fossies "Dox" file reference documentation and the last Fossies "Diffs" side-by-side code changes report: 372_vs_373.

    1 /* $XTermId: graphics_regis.c,v 1.130 2022/05/16 23:31:18 tom Exp $ */
    2 
    3 /*
    4  * Copyright 2014-2021,2022 by Ross Combs
    5  * Copyright 2014-2021,2022 by Thomas E. Dickey
    6  *
    7  *                         All Rights Reserved
    8  *
    9  * Permission is hereby granted, free of charge, to any person obtaining a
   10  * copy of this software and associated documentation files (the
   11  * "Software"), to deal in the Software without restriction, including
   12  * without limitation the rights to use, copy, modify, merge, publish,
   13  * distribute, sublicense, and/or sell copies of the Software, and to
   14  * permit persons to whom the Software is furnished to do so, subject to
   15  * the following conditions:
   16  *
   17  * The above copyright notice and this permission notice shall be included
   18  * in all copies or substantial portions of the Software.
   19  *
   20  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
   21  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
   22  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
   23  * IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY
   24  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
   25  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
   26  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
   27  *
   28  * Except as contained in this notice, the name(s) of the above copyright
   29  * holders shall not be used in advertising or otherwise to promote the
   30  * sale, use or other dealings in this Software without prior written
   31  * authorization.
   32  */
   33 
   34 #include <xterm.h>
   35 
   36 #include <stdio.h>
   37 #include <ctype.h>
   38 #include <math.h>
   39 #include <stdlib.h>
   40 
   41 #include <fontutils.h>
   42 #include <ptyx.h>
   43 
   44 #include <assert.h>
   45 #include <graphics.h>
   46 #include <graphics_regis.h>
   47 
   48 /* get rid of shadowing warnings (we will not draw Bessel functions) */
   49 #define y1 my_y1
   50 #define y0 my_y0
   51 
   52 #define SCALE_FIXED_POINT 16U
   53 
   54 #undef DEBUG_PARSING
   55 #undef DEBUG_ALPHABET_LOOKUP
   56 #undef DEBUG_ALPHABETS
   57 #undef DEBUG_BEZIER
   58 #undef DEBUG_SPLINE_SEGMENTS
   59 #undef DEBUG_SPLINE_POINTS
   60 #undef DEBUG_SPLINE_WITH_ROTATION
   61 #undef DEBUG_SPLINE_WITH_OVERDRAW
   62 #undef DEBUG_ARC_POINTS
   63 #undef DEBUG_ARC_CENTER
   64 #undef DEBUG_ARC_START
   65 #undef DEBUG_ARC_END
   66 #undef DEBUG_SPECIFIC_CHAR_METRICS
   67 #define IS_DEBUG_CHAR(CH) ((CH) == 'W')     /* glyphs to dump to terminal */
   68 #undef DEBUG_COMPUTED_FONT_METRICS
   69 #undef DEBUG_FONT_NAME
   70 #undef DEBUG_FONT_SIZE_SEARCH
   71 #undef DEBUG_XFT_GLYPH
   72 #undef DEBUG_GLYPH_RETRIEVAL
   73 #undef DEBUG_XFT_GLYPH_LOADING
   74 #undef DEBUG_LOAD
   75 
   76 /* controls for extensions over VT3x0 limitations */
   77 #define ENABLE_RGB_COLORSPECS
   78 #undef ENABLE_FREE_ROTATION
   79 #undef ENABLE_DISTORTIONLESS_ROTATION
   80 #define ENABLE_UPLOAD_ALPHABET_FROM_FONT
   81 #define ENABLE_UPLOAD_ALPHABET_ZERO
   82 #define ENABLE_USER_FONT_SIZE
   83 #define ENABLE_VARIABLE_ITALICS
   84 
   85 #define MIN_ITERATIONS_BEFORE_REFRESH 10U
   86 #define MIN_MS_BEFORE_REFRESH 33
   87 /* *INDENT-OFF* */
   88 typedef struct RegisPoint {
   89     int  x, y;
   90 } RegisPoint;
   91 
   92 typedef struct RegisWriteControls {
   93     unsigned     pv_multiplier;
   94     unsigned     pattern;
   95     unsigned     pattern_multiplier;
   96     unsigned     invert_pattern;
   97     unsigned     plane_mask;
   98     unsigned     write_style;
   99     RegisterNum  foreground;
  100     unsigned     shading_enabled;
  101     char         shading_character;
  102     int          shading_reference;
  103     unsigned     shading_reference_dim;
  104     unsigned     line_width;
  105 } RegisWriteControls;
  106 
  107 typedef struct RegisTextControls {
  108     unsigned  alphabet_num;
  109     unsigned  character_set_l; /* default: "(B" (ASCII) */
  110     unsigned  character_set_r; /* default: "-@" (Latin-1) */
  111     unsigned  character_display_w;
  112     unsigned  character_display_h;
  113     unsigned  character_unit_cell_w;
  114     unsigned  character_unit_cell_h;
  115     int       character_inc_x;
  116     int       character_inc_y;
  117     int       string_rotation;
  118     int       character_rotation;
  119     int       slant; /* for italic/oblique */
  120 } RegisTextControls;
  121 
  122 #define FixedCopy(dst, src, len) strncpy(dst, src, len - 1)[len - 1] = '\0'
  123 #define CopyFontname(dst, src) FixedCopy(dst, src, (size_t) REGIS_FONTNAME_LEN)
  124 
  125 #define MAX_REGIS_PAGES 8U
  126 
  127 #define MAX_REGIS_ALPHABETS 8U
  128 #define REGIS_ALPHABET_NAME_LEN 11U
  129 #define REGIS_FONTNAME_LEN 256U
  130 /* enough for a 16x24 font (about 100KB) */
  131 #define MAX_REGIS_ALPHABET_BYTES (256U * 16U * 24U)
  132 #define MAX_GLYPH_PIXELS 8192U
  133 #define MAX_GLYPHS 256U
  134 #define INVALID_ALPHABET_NUM ~0U
  135 
  136 typedef struct RegisAlphabet {
  137     unsigned       alphabet_num;
  138     unsigned       pixw, pixh;
  139     char           name[REGIS_ALPHABET_NAME_LEN];
  140     char           fontname[REGIS_FONTNAME_LEN];
  141     int            use_font;
  142     int            loaded[MAX_GLYPHS];
  143     Char          *bytes;
  144 } RegisAlphabet;
  145 
  146 typedef struct RegisDataFragment {
  147     char const  *start;
  148     unsigned     pos;
  149     unsigned     len;
  150 } RegisDataFragment;
  151 /* *INDENT-ON* */
  152 
  153 #define POSITION_STACK_SIZE 16U
  154 #define DUMMY_STACK_X -32768
  155 #define DUMMY_STACK_Y -32768
  156 
  157 #define CURVE_POSITION_ARC_EDGE     0U
  158 #define CURVE_POSITION_ARC_CENTER   1U
  159 #define CURVE_POSITION_OPEN_CURVE   2U
  160 #define CURVE_POSITION_CLOSED_CURVE 3U
  161 
  162 #define MAX_INPUT_CURVE_POINTS 16U
  163 #define MAX_CURVE_POINTS (MAX_INPUT_CURVE_POINTS + 4U)
  164 
  165 #define MAX_FILL_POINTS 2048U
  166 
  167 typedef struct RegisParseState {
  168     RegisDataFragment input;
  169     char *temp;
  170     unsigned templen;
  171     char command;
  172     char option;
  173     /* position stack */
  174     int stack_x[POSITION_STACK_SIZE];
  175     int stack_y[POSITION_STACK_SIZE];
  176     unsigned stack_next;    /* next empty position */
  177     /* curve options */
  178     int curve_mode;
  179     int arclen;
  180     int x_points[MAX_CURVE_POINTS];
  181     int y_points[MAX_CURVE_POINTS];
  182     unsigned num_points;
  183     /* load options */
  184     char load_name[REGIS_ALPHABET_NAME_LEN];
  185     unsigned load_alphabet;
  186     unsigned load_w, load_h;
  187     unsigned load_index;
  188     unsigned load_glyph;
  189     unsigned load_row;
  190     /* text options */
  191     unsigned text_tilt_state;
  192 } RegisParseState;
  193 
  194 #define TEXT_TILT_STATE_READY    0U
  195 #define TEXT_TILT_STATE_GOT_D    1U
  196 #define TEXT_TILT_STATE_GOT_DS   2U
  197 #define TEXT_TILT_STATE_GOT_DSD  3U
  198 
  199 typedef struct RegisGraphicsContext {
  200     XtermWidget current_widget;
  201     Graphic *destination_graphic;
  202     Graphic *display_graphic;
  203     int graphics_termid;
  204     int x_off, y_off;
  205     int x_div, y_div;
  206     int width, height;
  207     unsigned all_planes;
  208     RegisterNum background;
  209     char const *builtin_font;
  210     RegisAlphabet alphabets[MAX_REGIS_ALPHABETS];
  211     RegisWriteControls persistent_write_controls;
  212     RegisWriteControls temporary_write_controls;
  213     RegisTextControls persistent_text_controls;
  214     RegisTextControls temporary_text_controls;
  215     RegisTextControls *current_text_controls;
  216     int multi_input_mode;
  217     int graphics_output_cursor_x;
  218     int graphics_output_cursor_y;
  219     unsigned pattern_count;
  220     unsigned pattern_bit;
  221     int fill_mode;
  222     RegisPoint fill_points[MAX_FILL_POINTS];
  223     unsigned fill_point_count;
  224     unsigned destination_page;
  225     unsigned display_page;
  226     Boolean force_refresh;
  227 } RegisGraphicsContext;
  228 
  229 static RegisGraphicsContext persistent_context;
  230 static RegisParseState persistent_state;
  231 
  232 #define MAX_PATTERN_BITS 8U
  233 
  234 #define WRITE_STYLE_OVERLAY 1U
  235 #define WRITE_STYLE_REPLACE 2U
  236 #define WRITE_STYLE_COMPLEMENT 3U
  237 #define WRITE_STYLE_ERASE 4U
  238 
  239 #define WRITE_SHADING_REF_Y 0U
  240 #define WRITE_SHADING_REF_X 1U
  241 #define WRITE_SHADING_REF_NONE 2U
  242 
  243 /* keypress event example: http://iraf.net/forum/viewtopic.php?showtopic=61692 */
  244 
  245 #define MIN2(X, Y) ( (X) < (Y) ? (X) : (Y) )
  246 #define MIN3(X, Y, Z) ( MIN2(MIN2((X), (Y)), MIN2((Y), (Z))) )
  247 #define MAX2(X, Y) ( (X) > (Y) ? (X) : (Y) )
  248 #define MAX3(X, Y, Z) ( MAX2(MAX2((X), (Y)), MAX2((Y), (Z))) )
  249 
  250 #define ROT_LEFT_N(V, N) ( (((V) << ((N) & 3U )) & 255U) | \
  251                ((V) >> (8U - ((N) & 3U))) )
  252 #define ROT_LEFT(V) ( (((V) << 1U) & 255U) | ((V) >> 7U) )
  253 
  254 /* convert user coordinates to absolute pixel coordinates */
  255 #define SCALE_XCOORD(C, X, S) ( ( (X) * ((C)->width  - 1) ) / ( (C)->x_div * (S) ) )
  256 #define SCALE_YCOORD(C, Y, S) ( ( (Y) * ((C)->height - 1) ) / ( (C)->y_div * (S) ) )
  257 #define TRANSLATE_XCOORD(C, X, S) SCALE_XCOORD((C), (X) - (C)->x_off * (S), (S) )
  258 #define TRANSLATE_YCOORD(C, Y, S) SCALE_YCOORD((C), (Y) - (C)->y_off * (S), (S) )
  259 
  260 #if 0
  261 /* convert absolute pixel coordinate to user coordinates */
  262 #define SCALE_XPIX(C, X, S) ( ( (X) * ((C)->x_div * (S) ) ) / ((C)->width  - 1) )
  263 #define SCALE_YPIX(C, Y, S) ( ( (Y) * ((C)->y_div * (S) ) ) / ((C)->height - 1) )
  264 #define TRANSLATE_XPIX(C, X, S) ( SCALE_XPIX((C), (X), (S) ) + (C)->x_off * (S) )
  265 #define TRANSLATE_YPIX(C, Y, S) ( SCALE_YPIX((C), (Y), (S) ) + (C)->y_off * (S) )
  266 #endif
  267 
  268 #define READ_PIXEL(C, X, Y) read_pixel((C)->destination_graphic, (X), (Y))
  269 #define DRAW_PIXEL(C, X, Y, COL) draw_solid_pixel((C)->destination_graphic, (X), (Y), (COL))
  270 #define DRAW_ALL(C, COL) \
  271     draw_solid_rectangle((C)->destination_graphic, 0, 0, (C)->width, (C)->height, (COL))
  272 
  273 static unsigned get_shade_character_pixel(Char const *pixels,
  274                       unsigned w, unsigned h,
  275                       unsigned smaxf, unsigned scale,
  276                       int slant_dx, int px, int py);
  277 static void get_bitmap_of_character(RegisGraphicsContext const *context,
  278                     int ch, unsigned maxw, unsigned maxh,
  279                     Char *pixels,
  280                     unsigned *w, unsigned *h,
  281                     unsigned max_pixels);
  282 
  283 static void
  284 init_regis_load_state(RegisParseState *state)
  285 {
  286     state->load_index = MAX_REGIS_ALPHABETS;
  287     state->load_w = 8U;
  288     state->load_h = 10U;
  289     state->load_alphabet = 1U;  /* FIXME: is this the correct default */
  290     state->load_name[0] = '\0';
  291     state->load_glyph = (unsigned) (Char) '\0';
  292     state->load_row = 0U;
  293 }
  294 
  295 static void
  296 init_regis_parse_state(RegisParseState *state)
  297 {
  298     state->command = '_';
  299     state->option = '_';
  300     state->stack_next = 0U;
  301     state->load_index = MAX_REGIS_ALPHABETS;
  302     init_regis_load_state(state);
  303 }
  304 
  305 static int
  306 ifloor(double d)
  307 {
  308     double dl = floor(d);
  309     return (int) dl;
  310 }
  311 
  312 static int
  313 isqrt(double d)
  314 {
  315     double dl = sqrt(d);
  316     return (int) dl;
  317 }
  318 
  319 static void
  320 draw_regis_pixel(RegisGraphicsContext *context, int x, int y,
  321          unsigned value)
  322 {
  323     unsigned color = 0;
  324 
  325     switch (context->temporary_write_controls.write_style) {
  326     case WRITE_STYLE_OVERLAY:
  327     /*
  328      * Update pixels with foreground when pattern is 1,
  329      * don't change when pattern is 0.
  330      */
  331     if (!value) {
  332         return;
  333     }
  334 
  335     if (context->temporary_write_controls.invert_pattern) {
  336         color = context->background;
  337     } else {
  338         color = context->temporary_write_controls.foreground;
  339     }
  340     break;
  341 
  342     case WRITE_STYLE_REPLACE:
  343     /*
  344      * Update pixels with foreground when pattern is 1,
  345      * set to background when pattern is 0.
  346      */
  347     {
  348         unsigned fg, bg;
  349 
  350         if (context->temporary_write_controls.invert_pattern) {
  351         fg = context->background;
  352         bg = context->temporary_write_controls.foreground;
  353         } else {
  354         fg = context->temporary_write_controls.foreground;
  355         bg = context->background;
  356         }
  357         color = value ? fg : bg;
  358     }
  359     break;
  360 
  361     case WRITE_STYLE_COMPLEMENT:
  362     /*
  363      * Update pixels with background when pattern is 1,
  364      * don't change when pattern is 0.
  365      */
  366     if (!value) {
  367         return;
  368     }
  369 
  370     color = READ_PIXEL(context, x, y);
  371     if (color == COLOR_HOLE)
  372         color = context->background;
  373     color = color ^ context->all_planes;
  374     break;
  375 
  376     case WRITE_STYLE_ERASE:
  377     /* Update pixels to foreground. */
  378     if (context->temporary_write_controls.invert_pattern) {
  379         color = context->temporary_write_controls.foreground;
  380     } else {
  381         color = context->background;
  382     }
  383     break;
  384     }
  385 
  386     if (context->temporary_write_controls.plane_mask != context->all_planes) {
  387     unsigned old_color = READ_PIXEL(context, x, y);
  388     if (old_color == COLOR_HOLE)
  389         old_color = context->background;
  390     color = (color & context->temporary_write_controls.plane_mask) |
  391         (old_color & ~context->temporary_write_controls.plane_mask);
  392     }
  393 
  394     DRAW_PIXEL(context, x, y, color);
  395 }
  396 
  397 static void
  398 shade_pattern_to_pixel(RegisGraphicsContext *context, unsigned dim, int ref,
  399                int x, int y)
  400 {
  401     unsigned value;
  402 
  403     if (dim == WRITE_SHADING_REF_X) {
  404     int delta = x > ref ? 1 : -1;
  405     int curr_x;
  406 
  407     context->pattern_bit = 1U << (((unsigned) y) & 7U);
  408     for (curr_x = ref; curr_x != x + delta; curr_x += delta) {
  409         value = context->temporary_write_controls.pattern &
  410         context->pattern_bit;
  411         draw_regis_pixel(context, curr_x, y, value);
  412     }
  413     } else if (dim == WRITE_SHADING_REF_Y) {
  414     int delta = y > ref ? 1 : -1;
  415     int curr_y;
  416 
  417     for (curr_y = ref; curr_y != y + delta; curr_y += delta) {
  418         context->pattern_bit = 1U << (((unsigned) curr_y) & 7U);
  419         value = context->temporary_write_controls.pattern &
  420         context->pattern_bit;
  421         draw_regis_pixel(context, x, curr_y, value);
  422     }
  423     } else {
  424     TRACE(("ERROR: shading requested, but there is no reference axis\n"));
  425     }
  426 }
  427 
  428 static void
  429 shade_char_to_pixel(RegisGraphicsContext *context, Char const *pixels,
  430             unsigned w, unsigned h, unsigned dim, int ref, int x, int y)
  431 {
  432     unsigned xmaxf = context->current_text_controls->character_unit_cell_w;
  433     unsigned ymaxf = context->current_text_controls->character_unit_cell_h;
  434     unsigned smaxf;
  435     unsigned s;
  436     unsigned scale;
  437     unsigned value;
  438 
  439     if (xmaxf > ymaxf) {
  440     smaxf = ymaxf;
  441     s = h;
  442     } else {
  443     smaxf = xmaxf;
  444     s = w;
  445     }
  446     scale = (s << SCALE_FIXED_POINT) / smaxf;
  447 
  448     if (dim == WRITE_SHADING_REF_X) {
  449     int delta = x > ref ? 1 : -1;
  450     int curr_x;
  451 
  452     for (curr_x = ref; curr_x != x + delta; curr_x += delta) {
  453         value = get_shade_character_pixel(pixels, w, h, smaxf, scale, 0,
  454                           curr_x, y);
  455         draw_regis_pixel(context, curr_x, y, value);
  456     }
  457     } else if (dim == WRITE_SHADING_REF_Y) {
  458     int delta = y > ref ? 1 : -1;
  459     int curr_y;
  460 
  461     for (curr_y = ref; curr_y != y + delta; curr_y += delta) {
  462         value = get_shade_character_pixel(pixels, w, h, smaxf, scale, 0, x,
  463                           curr_y);
  464         draw_regis_pixel(context, x, curr_y, value);
  465     }
  466     } else {
  467     TRACE(("ERROR: shading requested, but there is no reference axis\n"));
  468     }
  469 }
  470 
  471 static void
  472 draw_patterned_pixel(RegisGraphicsContext *context, int x, int y)
  473 {
  474     if (context->pattern_count >=
  475     context->temporary_write_controls.pattern_multiplier) {
  476     context->pattern_count = 0U;
  477     context->pattern_bit = ROT_LEFT(context->pattern_bit);
  478     }
  479     context->pattern_count++;
  480 
  481     draw_regis_pixel(context, x, y,
  482              context->temporary_write_controls.pattern &
  483              context->pattern_bit);
  484 }
  485 
  486 static void
  487 shade_to_pixel(RegisGraphicsContext *context, unsigned dim, int ref,
  488            int x, int y)
  489 {
  490     if (context->temporary_write_controls.shading_character != '\0') {
  491     unsigned xmaxf = context->current_text_controls->character_unit_cell_w;
  492     unsigned ymaxf = context->current_text_controls->character_unit_cell_h;
  493     char ch = context->temporary_write_controls.shading_character;
  494     Char pixels[MAX_GLYPH_PIXELS];
  495     unsigned w, h;
  496 
  497     get_bitmap_of_character(context, ch, xmaxf, ymaxf, pixels, &w, &h,
  498                 MAX_GLYPH_PIXELS);
  499     if (w > 0 && h > 0) {
  500         shade_char_to_pixel(context, pixels, w, h, dim, ref, x, y);
  501     }
  502     } else {
  503     shade_pattern_to_pixel(context, dim, ref, x, y);
  504     }
  505 }
  506 
  507 static void
  508 draw_or_save_patterned_pixel(RegisGraphicsContext *context, int x, int y)
  509 {
  510     if (context->fill_mode == 1) {
  511     if (context->fill_point_count >= MAX_FILL_POINTS) {
  512         TRACE(("point %d,%d can not be added to filled polygon\n",
  513            x, y));
  514         return;
  515     }
  516     if (context->fill_point_count > 0U &&
  517         context->fill_points[context->fill_point_count - 1U].x == x &&
  518         context->fill_points[context->fill_point_count - 1U].y == y) {
  519         return;
  520     }
  521     context->fill_points[context->fill_point_count].x = x;
  522     context->fill_points[context->fill_point_count].y = y;
  523     context->fill_point_count++;
  524     return;
  525     }
  526 
  527     if (context->temporary_write_controls.shading_enabled) {
  528     unsigned dim = context->temporary_write_controls.shading_reference_dim;
  529     int ref = context->temporary_write_controls.shading_reference;
  530 
  531     shade_to_pixel(context, dim, ref, x, y);
  532     return;
  533     }
  534 
  535     draw_patterned_pixel(context, x, y);
  536 }
  537 
  538 static int
  539 sort_points(void const *l, void const *r)
  540 {
  541     RegisPoint const *const lp = (RegisPoint const *)l;
  542     RegisPoint const *const rp = (RegisPoint const *)r;
  543 
  544     if (lp->y < rp->y)
  545     return -1;
  546     if (lp->y > rp->y)
  547     return +1;
  548     if (lp->x < rp->x)
  549     return -1;
  550     if (lp->x > rp->x)
  551     return +1;
  552     return 0;
  553 }
  554 
  555 static void
  556 draw_shaded_polygon(RegisGraphicsContext *context)
  557 {
  558     unsigned p;
  559     int old_x, old_y;
  560     int inside;
  561     Char pixels[MAX_GLYPH_PIXELS];
  562     unsigned w = 1, h = 1;
  563 
  564     char ch = context->temporary_write_controls.shading_character;
  565     unsigned xmaxf = context->current_text_controls->character_unit_cell_w;
  566     unsigned ymaxf = context->current_text_controls->character_unit_cell_h;
  567 
  568     get_bitmap_of_character(context, ch, xmaxf, ymaxf, pixels, &w, &h,
  569                 MAX_GLYPH_PIXELS);
  570     if (w < 1U || h < 1U) {
  571     return;
  572     }
  573 
  574     qsort(context->fill_points, (size_t) context->fill_point_count,
  575       sizeof(context->fill_points[0]), sort_points);
  576 
  577     old_x = DUMMY_STACK_X;
  578     old_y = DUMMY_STACK_Y;
  579     inside = 0;
  580     for (p = 0U; p < context->fill_point_count; p++) {
  581     int new_x = context->fill_points[p].x;
  582     int new_y = context->fill_points[p].y;
  583 #if 0
  584     printf("got %d,%d (%d,%d) inside=%d\n", new_x, new_y, old_x, old_y, inside);
  585 #endif
  586 
  587     /*
  588      * FIXME: This is using pixels to represent lines which loses
  589      * information about exact slope and how many lines are present which
  590      * causes misbehavior with some inputs (especially complex polygons).
  591      * It also takes more room than remembering vertices, but I'd rather
  592      * not have to implement line segments for arcs.  Maybe store a count
  593      * at each vertex instead (doesn't fix the slope problem).
  594      */
  595     /*
  596      * FIXME: Change this to only draw inside of polygons, and round
  597      * points in a uniform direction to avoid overlapping drawing.  As an
  598      * option we could continue to support drawing the outline.
  599      */
  600     if (new_y != old_y) {
  601         if (inside) {
  602         /*
  603          * Just draw the vertical line when there is not a matching
  604          * edge on the right side.
  605          */
  606         shade_char_to_pixel(context, pixels, w, h,
  607                     WRITE_SHADING_REF_X,
  608                     old_x, old_x, old_y);
  609         }
  610         inside = 1;
  611     } else {
  612         if (inside) {
  613         shade_char_to_pixel(context, pixels, w, h,
  614                     WRITE_SHADING_REF_X,
  615                     old_x, new_x, new_y);
  616         }
  617         if (new_x > old_x + 1) {
  618         inside = !inside;
  619         }
  620     }
  621 
  622     old_x = new_x;
  623     old_y = new_y;
  624     }
  625 
  626     context->destination_graphic->dirty = True;
  627 }
  628 
  629 static void
  630 draw_filled_polygon(RegisGraphicsContext *context)
  631 {
  632     unsigned p;
  633     int old_x, old_y;
  634     int inside;
  635 
  636     qsort(context->fill_points, (size_t) context->fill_point_count,
  637       sizeof(context->fill_points[0]), sort_points);
  638 
  639     old_x = DUMMY_STACK_X;
  640     old_y = DUMMY_STACK_Y;
  641     inside = 0;
  642     for (p = 0U; p < context->fill_point_count; p++) {
  643     int new_x = context->fill_points[p].x;
  644     int new_y = context->fill_points[p].y;
  645 #if 0
  646     printf("got %d,%d (%d,%d) inside=%d\n", new_x, new_y, old_x, old_y, inside);
  647 #endif
  648 
  649     /*
  650      * FIXME: This is using pixels to represent lines which loses
  651      * information about exact slope and how many lines are present which
  652      * causes misbehavior with some inputs (especially complex polygons).
  653      * It also takes more room than remembering vertices, but I'd rather
  654      * not have to implement line segments for arcs.  Maybe store a count
  655      * at each vertex instead (doesn't fix the slope problem).
  656      */
  657     /*
  658      * FIXME: Change this to only draw inside of polygons, and round
  659      * points in a uniform direction to avoid overlapping drawing.  As an
  660      * option we could continue to support drawing the outline.
  661      */
  662     if (new_y != old_y) {
  663         if (inside) {
  664         /*
  665          * Just draw the vertical line when there is not a matching
  666          * edge on the right side.
  667          */
  668         shade_pattern_to_pixel(context, WRITE_SHADING_REF_X,
  669                        old_x, old_x, old_y);
  670         }
  671         inside = 1;
  672     } else {
  673         if (inside) {
  674         shade_pattern_to_pixel(context, WRITE_SHADING_REF_X,
  675                        old_x, new_x, new_y);
  676         }
  677         if (new_x > old_x + 1) {
  678         inside = !inside;
  679         }
  680     }
  681 
  682     old_x = new_x;
  683     old_y = new_y;
  684     }
  685 
  686     context->destination_graphic->dirty = True;
  687 }
  688 
  689 static void
  690 draw_patterned_line(RegisGraphicsContext *context, int x1, int y1,
  691             int x2, int y2)
  692 {
  693     int x, y;
  694     int dx, dy;
  695     int dir, diff;
  696 
  697     dx = abs(x1 - x2);
  698     dy = abs(y1 - y2);
  699 
  700     if (dx > dy) {
  701     if (x1 > x2) {
  702         int tmp;
  703         EXCHANGE(x1, x2, tmp);
  704         EXCHANGE(y1, y2, tmp);
  705     }
  706     if (y1 < y2)
  707         dir = 1;
  708     else if (y1 > y2)
  709         dir = -1;
  710     else
  711         dir = 0;
  712 
  713     diff = 0;
  714     y = y1;
  715     for (x = x1; x <= x2; x++) {
  716         if (diff >= dx) {
  717         diff -= dx;
  718         y += dir;
  719         }
  720         diff += dy;
  721         draw_or_save_patterned_pixel(context, x, y);
  722     }
  723     } else {
  724     if (y1 > y2) {
  725         int tmp;
  726         EXCHANGE(y1, y2, tmp);
  727         EXCHANGE(x1, x2, tmp);
  728     }
  729     if (x1 < x2)
  730         dir = 1;
  731     else if (x1 > x2)
  732         dir = -1;
  733     else
  734         dir = 0;
  735 
  736     diff = 0;
  737     x = x1;
  738     for (y = y1; y <= y2; y++) {
  739         if (diff >= dy) {
  740         diff -= dy;
  741         x += dir;
  742         }
  743         diff += dx;
  744         draw_or_save_patterned_pixel(context, x, y);
  745     }
  746     }
  747 
  748     context->destination_graphic->dirty = True;
  749 }
  750 
  751 typedef struct {
  752     int dxx;
  753     int dxy;
  754     int dyx;
  755     int dyy;
  756 } quadmap_coords;
  757 
  758 static void
  759 draw_patterned_arc(RegisGraphicsContext *context,
  760            int cx, int cy,
  761            int ex, int ey,
  762            int a_start, int a_length,
  763            int *ex_final, int *ey_final)
  764 {
  765     const double third = hypot((double) (cx - ex), (double) (cy - ey));
  766     const int radius = (int) third;
  767     const int ra = radius;
  768     const int rb = radius;
  769     const quadmap_coords neg_quadmap[4] =
  770     {
  771     {-1, 0, 0, +1},
  772     {0, -1, -1, 0},
  773     {+1, 0, 0, -1},
  774     {0, +1, +1, 0},
  775     };
  776     const quadmap_coords pos_quadmap[4] =
  777     {
  778     {-1, 0, 0, -1},
  779     {0, -1, +1, 0},
  780     {+1, 0, 0, +1},
  781     {0, +1, -1, 0},
  782     };
  783     const quadmap_coords *quadmap;
  784     int total_points;
  785     int half_degree;
  786     int points_start, points_stop;
  787     int points;
  788     unsigned iterations;
  789     long rx, ry;
  790     long dx, dy;
  791     int x, y;
  792     long e2;
  793     long error;
  794 
  795     TRACE(("orig a_length=%d a_start=%d\n", a_length, a_start));
  796     if (a_length == 0)
  797     return;
  798     if (a_length > 0) {
  799     quadmap = pos_quadmap;
  800     } else {
  801     quadmap = neg_quadmap;
  802     if (a_start != 0)
  803         a_start = 3600 - a_start;
  804     a_length = abs(a_length);
  805     }
  806     TRACE(("positive a_length=%d a_start=%d\n", a_length, a_start));
  807 
  808     rx = -ra;
  809     ry = 0;
  810     e2 = rb;
  811     dx = (2 * rx + 1) * e2 * e2;
  812     dy = rx * rx;
  813     error = dx + dy;
  814     total_points = 0;
  815     do {
  816     total_points += 4;
  817     e2 = 2 * error;
  818     if (e2 >= dx) {
  819         rx++;
  820         dx += 2 * rb * rb;
  821         error += dx;
  822     }
  823     if (e2 <= dy) {
  824         ry++;
  825         dy += 2 * ra * ra;
  826         error += dy;
  827     }
  828     }
  829     while (rx <= 0);
  830 
  831     /* FIXME: This is apparently not accurate enough because some arcs start or
  832      * end a few pixels off.  Maybe compare line slopes in the loop below
  833      * instead?
  834      */
  835     half_degree = total_points * 5;
  836     points_start = (total_points * a_start - half_degree) / 3600;
  837     points_stop = (total_points * a_start +
  838            total_points * a_length + half_degree) / 3600;
  839     TRACE(("drawing arc with %d points clockwise from %g degrees for %g degrees (from point %d to %d out of %d)\n",
  840        total_points, a_start / 10.0, a_length / 10.0, points_start, points_stop,
  841        total_points));
  842 
  843     /* FIXME: The four pixels at the cardinal directions are double-drawn. */
  844     points = 0;
  845     for (iterations = 0U; iterations < 8U; iterations++) {
  846     int q2 = iterations & 0x3;
  847 
  848     rx = -ra;
  849     ry = 0;
  850     e2 = rb;
  851     dx = (2 * rx + 1) * e2 * e2;
  852     dy = rx * rx;
  853     error = dx + dy;
  854     do {
  855 #ifdef DEBUG_ARC_POINTS
  856         double rad = atan2(
  857                   (double) (quadmap[q2].dyx * rx +
  858                         quadmap[q2].dyy * ry),
  859                   (double) (quadmap[q2].dxx * rx +
  860                         quadmap[q2].dxy * ry));
  861         double deg = (360.0 * rad / (2.0 * M_PI));
  862         if (deg < 0.0)
  863         deg += 360.0;
  864 #endif
  865 
  866         if (points >= points_start && points <= points_stop) {
  867         x = (int) (cx +
  868                quadmap[q2].dxx * rx +
  869                quadmap[q2].dxy * ry);
  870         y = (int) (cy +
  871                quadmap[q2].dyx * rx +
  872                quadmap[q2].dyy * ry);
  873 #ifdef DEBUG_ARC_POINTS
  874         TRACE(("drawing point %u at %d,%d (%.5g deg)\n",
  875                points, x, y, deg));
  876 #endif
  877         draw_or_save_patterned_pixel(context, x, y);
  878         if (ex_final)
  879             *ex_final = x;
  880         if (ey_final)
  881             *ey_final = y;
  882         } else {
  883 #ifdef DEBUG_ARC_POINTS
  884         x = (int) (cx + quadmap[q2].dxx * rx + quadmap[q2].dxy * ry);
  885         y = (int) (cy + quadmap[q2].dyx * rx + quadmap[q2].dyy * ry);
  886         TRACE(("skipping point %u at %d,%d which is outside of range (%.5g deg)\n",
  887                points, x, y, deg));
  888 #endif
  889         }
  890         points++;
  891 
  892         e2 = 2 * error;
  893         if (e2 >= dx) {
  894         rx++;
  895         dx += 2 * rb * rb;
  896         error += dx;
  897         }
  898         if (e2 <= dy) {
  899         ry++;
  900         dy += 2 * ra * ra;
  901         error += dy;
  902         }
  903     }
  904     while (rx <= 0);
  905     }
  906 
  907     context->destination_graphic->dirty = True;
  908 }
  909 
  910 /*
  911  * The plot* functions are based on optimized rasterization primitives written
  912  * by Zingl Alois.
  913  * See http://members.chello.at/easyfilter/bresenham.html
  914  */
  915 
  916 /*
  917  * FIXME:
  918  * This is a terrible temporary hack.  The plot functions below can be adapted
  919  * to work like the other rasterization functions but there's no point in doing
  920  * that until we know we don't have to write something completely different.
  921  */
  922 static RegisGraphicsContext *global_context;
  923 static void
  924 setPixel(int x, int y)
  925 {
  926     draw_or_save_patterned_pixel(global_context, x, y);
  927 }
  928 
  929 static void
  930 plotLine(int x0, int y0, int x1, int y1)
  931 {
  932     int dx = abs(x1 - x0), sx = x0 < x1 ? 1 : -1;
  933     int dy = -abs(y1 - y0), sy = y0 < y1 ? 1 : -1;
  934     int err = dx + dy;      /* error value e_xy */
  935 
  936     for (;;) {          /* loop */
  937     int e2;
  938     setPixel(x0, y0);
  939     e2 = 2 * err;
  940     if (e2 >= dy) {     /* e_xy+e_x > 0 */
  941         if (x0 == x1)
  942         break;
  943         err += dy;
  944         x0 += sx;
  945     }
  946     if (e2 <= dx) {     /* e_xy+e_y < 0 */
  947         if (y0 == y1)
  948         break;
  949         err += dx;
  950         y0 += sy;
  951     }
  952     }
  953 }
  954 
  955 static void
  956 plotQuadBezierSeg(int x0, int y0, int x1, int y1, int x2, int y2)
  957 {               /* plot a limited quadratic Bezier segment */
  958     int sx = x2 - x1;
  959     int sy = y2 - y1;
  960     long xx = (x0 - x1);    /* relative values for checks */
  961     long yy = (y0 - y1);
  962     double cur = (double) (xx * sy - yy * sx);  /* curvature */
  963 
  964     assert(xx * sx <= 0 && yy * sy <= 0);   /* sign of gradient must not change */
  965 
  966     if (sx * (long) sx + sy * (long) sy > xx * xx + yy * yy) {  /* begin with longer part */
  967     x2 = x0;
  968     x0 = sx + x1;
  969     y2 = y0;
  970     y0 = sy + y1;
  971     cur = -cur;     /* swap P0 P2 */
  972     }
  973     if (cur != 0.0) {       /* no straight line */
  974     long xy;
  975     double dx, dy, err;
  976 
  977     xx += sx;
  978     xx *= (sx = (x0 < x2) ? 1 : -1);    /* x step direction */
  979     yy += sy;
  980     yy *= (sy = (y0 < y2) ? 1 : -1);    /* y step direction */
  981     xy = 2 * xx * yy;
  982     xx *= xx;
  983     yy *= yy;       /* differences 2nd degree */
  984     if (cur * sx * sy < 0) {    /* negated curvature? */
  985         xx = -xx;
  986         yy = -yy;
  987         xy = -xy;
  988         cur = -cur;
  989     }
  990     /* differences 1st degree */
  991     dx = ((4.0 * sy * cur * (x1 - x0)) + (double) xx) - (double) xy;
  992     dy = ((4.0 * sx * cur * (y0 - y1)) + (double) yy) - (double) xy;
  993     xx += xx;
  994     yy += yy;
  995     err = dx + dy + (double) xy;    /* error 1st step */
  996     do {
  997         setPixel(x0, y0);   /* plot curve */
  998         if (x0 == x2 && y0 == y2)
  999         return;     /* last pixel -> curve finished */
 1000         y1 = (2 * err) < dx;    /* save value for test of y step */
 1001         if ((2 * err) > dy) {
 1002         x0 += sx;
 1003         dx -= (double) xy;
 1004         dy += (double) yy;
 1005         err += dy;
 1006         }           /* x step */
 1007         if (y1) {
 1008         y0 += sy;
 1009         dy -= (double) xy;
 1010         dx += (double) xx;
 1011         err += dx;
 1012         }           /* y step */
 1013     } while (dy < 0 && dx > 0); /* gradient negates -> algorithm fails */
 1014     }
 1015     plotLine(x0, y0, x2, y2);   /* plot remaining part to end */
 1016 }
 1017 
 1018 #if 0
 1019 static void
 1020 plotQuadBezier(int x0, int y0, int x1, int y1, int x2, int y2)
 1021 {               /* plot any quadratic Bezier curve */
 1022     int x = x0 - x1;
 1023     int y = y0 - y1;
 1024     double t = x0 - 2 * x1 + x2;
 1025     double r;
 1026 
 1027     if ((long) x * (x2 - x1) > 0) { /* horizontal cut at P4? */
 1028     if ((long) y * (y2 - y1) > 0)   /* vertical cut at P6 too? */
 1029         if (fabs((y0 - 2 * y1 + y2) / t * x) > abs(y)) {    /* which first? */
 1030         x0 = x2;
 1031         x2 = x + x1;
 1032         y0 = y2;
 1033         y2 = y + y1;    /* swap points */
 1034         }           /* now horizontal cut at P4 comes first */
 1035     t = (x0 - x1) / t;
 1036     r = (1 - t) * ((1 - t) * y0 + 2.0 * t * y1) + t * t * y2;   /* By(t=P4) */
 1037     t = (x0 * x2 - x1 * x1) * t / (x0 - x1);    /* gradient dP4/dx=0 */
 1038     x = ifloor(t + 0.5);
 1039     y = ifloor(r + 0.5);
 1040     r = (y1 - y0) * (t - x0) / (x1 - x0) + y0;  /* intersect P3 | P0 P1 */
 1041     plotQuadBezierSeg(x0, y0, x, ifloor(r + 0.5), x, y);
 1042     r = (y1 - y2) * (t - x2) / (x1 - x2) + y2;  /* intersect P4 | P1 P2 */
 1043     x0 = x1 = x;
 1044     y0 = y;
 1045     y1 = ifloor(r + 0.5);   /* P0 = P4, P1 = P8 */
 1046     }
 1047     if ((long) (y0 - y1) * (y2 - y1) > 0) { /* vertical cut at P6? */
 1048     t = y0 - 2 * y1 + y2;
 1049     t = (y0 - y1) / t;
 1050     r = (1 - t) * ((1 - t) * x0 + 2.0 * t * x1) + t * t * x2;   /* Bx(t=P6) */
 1051     t = (y0 * y2 - y1 * y1) * t / (y0 - y1);    /* gradient dP6/dy=0 */
 1052     x = ifloor(r + 0.5);
 1053     y = ifloor(t + 0.5);
 1054     r = (x1 - x0) * (t - y0) / (y1 - y0) + x0;  /* intersect P6 | P0 P1 */
 1055     plotQuadBezierSeg(x0, y0, ifloor(r + 0.5), y, x, y);
 1056     r = (x1 - x2) * (t - y2) / (y1 - y2) + x2;  /* intersect P7 | P1 P2 */
 1057     x0 = x;
 1058     x1 = ifloor(r + 0.5);
 1059     y0 = y1 = y;        /* P0 = P6, P1 = P7 */
 1060     }
 1061     plotQuadBezierSeg(x0, y0, x1, y1, x2, y2);  /* remaining part */
 1062 }
 1063 #endif
 1064 
 1065 static void
 1066 plotCubicBezierSeg(int x0, int y0,
 1067            double x1, double y1,
 1068            double x2, double y2,
 1069            int x3, int y3)
 1070 {               /* plot limited cubic Bezier segment */
 1071     int f, fx, fy, tt;
 1072     int leg = 1;
 1073     int sx = x0 < x3 ? 1 : -1;
 1074     int sy = y0 < y3 ? 1 : -1;  /* step direction */
 1075     double xc = -fabs(x0 + x1 - x2 - x3);
 1076     double xa = xc - 4 * sx * (x1 - x2);
 1077     double xb = sx * (x0 - x1 - x2 + x3);
 1078     double yc = -fabs(y0 + y1 - y2 - y3);
 1079     double ya = yc - 4 * sy * (y1 - y2);
 1080     double yb = sy * (y0 - y1 - y2 + y3);
 1081     double ab, ac, bc, cb, xx, xy, yy, dx, dy, ex, *pxy;
 1082     double EP = 0.01;
 1083     /* check for curve restrains */
 1084     /* slope P0-P1 == P2-P3    and  (P0-P3 == P1-P2      or   no slope change) */
 1085     assert((x1 - x0) * (x2 - x3) < EP &&
 1086        ((x3 - x0) * (x1 - x2) < EP || xb * xb < xa * xc + EP));
 1087     assert((y1 - y0) * (y2 - y3) < EP &&
 1088        ((y3 - y0) * (y1 - y2) < EP || yb * yb < ya * yc + EP));
 1089 
 1090     if (xa == 0.0 && ya == 0.0) {   /* quadratic Bezier */
 1091     sx = ifloor((3 * x1 - x0 + 1) / 2);
 1092     sy = ifloor((3 * y1 - y0 + 1) / 2); /* new midpoint */
 1093     plotQuadBezierSeg(x0, y0, sx, sy, x3, y3);
 1094     return;
 1095     }
 1096     x1 = (x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0) + 1; /* line lengths */
 1097     x2 = (x2 - x3) * (x2 - x3) + (y2 - y3) * (y2 - y3) + 1;
 1098     do {            /* loop over both ends */
 1099     ab = xa * yb - xb * ya;
 1100     ac = xa * yc - xc * ya;
 1101     bc = xb * yc - xc * yb;
 1102     ex = ab * (ab + ac - 3 * bc) + ac * ac;     /* P0 part of self-intersection loop? */
 1103     f = ((ex > 0.0)
 1104          ? 1
 1105          : isqrt(1 + 1024 / x1));   /* calculate resolution */
 1106     ab *= f;
 1107     ac *= f;
 1108     bc *= f;
 1109     ex *= f * f;        /* increase resolution */
 1110     xy = 9 * (ab + ac + bc) / 8;
 1111     cb = 8 * (xa - ya); /* init differences of 1st degree */
 1112     dx = 27 * (8 * ab * (yb * yb - ya * yc) +
 1113            ex * (ya + 2 * yb + yc)) / 64 - ya * ya * (xy - ya);
 1114     dy = 27 * (8 * ab * (xb * xb - xa * xc) -
 1115            ex * (xa + 2 * xb + xc)) / 64 - xa * xa * (xy + xa);
 1116     /* init differences of 2nd degree */
 1117     xx = 3 * (3 * ab * (3 * yb * yb - ya * ya - 2 * ya * yc) -
 1118           ya * (3 * ac * (ya + yb) + ya * cb)) / 4;
 1119     yy = 3 * (3 * ab * (3 * xb * xb - xa * xa - 2 * xa * xc) -
 1120           xa * (3 * ac * (xa + xb) + xa * cb)) / 4;
 1121     xy = xa * ya * (6 * ab + 6 * ac - 3 * bc + cb);
 1122     ac = ya * ya;
 1123     cb = xa * xa;
 1124     xy = 3 * (xy + 9 * f * (cb * yb * yc - xb * xc * ac) -
 1125           18 * xb * yb * ab) / 8;
 1126 
 1127     if (ex < 0) {       /* negate values if inside self-intersection loop */
 1128         dx = -dx;
 1129         dy = -dy;
 1130         xx = -xx;
 1131         yy = -yy;
 1132         xy = -xy;
 1133         ac = -ac;
 1134         cb = -cb;
 1135     }           /* init differences of 3rd degree */
 1136     ab = 6 * ya * ac;
 1137     ac = -6 * xa * ac;
 1138     bc = 6 * ya * cb;
 1139     cb = -6 * xa * cb;
 1140     dx += xy;
 1141     ex = dx + dy;
 1142     dy += xy;       /* error of 1st step */
 1143 
 1144     for (pxy = &xy, fx = fy = f; x0 != x3 && y0 != y3;) {
 1145         setPixel(x0, y0);   /* plot curve */
 1146         do {        /* move sub-steps of one pixel */
 1147         if (dx > *pxy || dy < *pxy)
 1148             goto exit;  /* confusing values */
 1149         y1 = 2 * ex - dy;   /* save value for test of y step */
 1150         if (2 * ex >= dx) { /* x sub-step */
 1151             fx--;
 1152             ex += dx += xx;
 1153             dy += xy += ac;
 1154             yy += bc;
 1155             xx += ab;
 1156         }
 1157         if (y1 <= 0) {  /* y sub-step */
 1158             fy--;
 1159             ex += dy += yy;
 1160             dx += xy += bc;
 1161             xx += ac;
 1162             yy += cb;
 1163         }
 1164         } while (fx > 0 && fy > 0);     /* pixel complete? */
 1165         if (2 * fx <= f) {
 1166         x0 += sx;
 1167         fx += f;
 1168         }           /* x step */
 1169         if (2 * fy <= f) {
 1170         y0 += sy;
 1171         fy += f;
 1172         }           /* y step */
 1173         if (pxy == &xy && dx < 0 && dy > 0)
 1174         pxy = &EP;  /* pixel ahead valid */
 1175     }
 1176       exit:
 1177     EXCHANGE(x0, x3, tt);
 1178     sx = -sx;
 1179     xb = -xb;       /* swap legs */
 1180     EXCHANGE(y0, y3, tt);
 1181     sy = -sy;
 1182     yb = -yb;
 1183     x1 = x2;
 1184     } while (leg--);        /* try other end */
 1185     plotLine(x0, y0, x3, y3);   /* remaining part in case of cusp or crunode */
 1186 }
 1187 
 1188 static void
 1189 plotCubicBezier(int x0, int y0, int x1, int y1,
 1190         int x2, int y2, int x3, int y3)
 1191 {               /* plot any cubic Bezier curve */
 1192     int n = 0, i = 0;
 1193     long xc = x0 + x1 - x2 - x3;
 1194     long xa = xc - 4 * (x1 - x2);
 1195     long xb = x0 - x1 - x2 + x3;
 1196     long xd = xb + 4 * (x1 + x2);
 1197     long yc = y0 + y1 - y2 - y3;
 1198     long ya = yc - 4 * (y1 - y2);
 1199     long yb = y0 - y1 - y2 + y3;
 1200     long yd = yb + 4 * (y1 + y2);
 1201     double fx0 = x0;
 1202     double fy0 = y0;
 1203     double t1 = (double) (xb * xb - xa * xc), t2, t[5];
 1204 
 1205 #ifdef DEBUG_BEZIER
 1206     printf("plotCubicBezier(%d,%d, %d,%d, %d,%d, %d,%d\n",
 1207        x0, y0, x1, y1, x2, y2, x3, y3);
 1208 #endif
 1209     /* sub-divide curve at gradient sign changes */
 1210     if (xa == 0) {      /* horizontal */
 1211     if (labs(xc) < 2 * labs(xb))
 1212         t[n++] = (double) xc / (2.0 * (double) xb);     /* one change */
 1213     } else if (t1 > 0.0) {  /* two changes */
 1214     t2 = sqrt(t1);
 1215     t1 = ((double) xb - t2) / (double) xa;
 1216     if (fabs(t1) < 1.0)
 1217         t[n++] = t1;
 1218     t1 = ((double) xb + t2) / (double) xa;
 1219     if (fabs(t1) < 1.0)
 1220         t[n++] = t1;
 1221     }
 1222     t1 = (double) (yb * yb - ya * yc);
 1223     if (ya == 0) {      /* vertical */
 1224     if (labs(yc) < 2 * labs(yb))
 1225         t[n++] = (double) yc / (2.0 * (double) yb);     /* one change */
 1226     } else if (t1 > 0.0) {  /* two changes */
 1227     t2 = sqrt(t1);
 1228     t1 = ((double) yb - t2) / (double) ya;
 1229     if (fabs(t1) < 1.0)
 1230         t[n++] = t1;
 1231     t1 = ((double) yb + t2) / (double) ya;
 1232     if (fabs(t1) < 1.0)
 1233         t[n++] = t1;
 1234     }
 1235     for (i = 1; i < n; i++) /* bubble sort of 4 points */
 1236     if ((t1 = t[i - 1]) > t[i]) {
 1237         t[i - 1] = t[i];
 1238         t[i] = t1;
 1239         i = 0;
 1240     }
 1241 
 1242     t1 = -1.0;
 1243     t[n] = 1.0;         /* begin / end point */
 1244     for (i = 0; i <= n; i++) {  /* plot each segment separately */
 1245     double fx1, fx2, fx3;
 1246     double fy1, fy2, fy3;
 1247 
 1248     t2 = t[i];      /* sub-divide at t[i-1], t[i] */
 1249     fx1 = (t1 * (t1 * (double) xb - (double) (2 * xc)) -
 1250            t2 * (t1 * (t1 * (double) xa - (double) (2 * xb)) + (double)
 1251              xc) + (double) xd) / 8 - fx0;
 1252     fy1 = (t1 * (t1 * (double) yb - (double) (2 * yc)) -
 1253            t2 * (t1 * (t1 * (double) ya - (double) (2 * yb)) + (double)
 1254              yc) + (double) yd) / 8 - fy0;
 1255     fx2 = (t2 * (t2 * (double) xb - (double) (2 * xc)) -
 1256            t1 * (t2 * (t2 * (double) xa - (double) (2 * xb)) + (double)
 1257              xc) + (double) xd) / 8 - fx0;
 1258     fy2 = (t2 * (t2 * (double) yb - (double) (2 * yc)) -
 1259            t1 * (t2 * (t2 * (double) ya - (double) (2 * yb)) + (double)
 1260              yc) + (double) yd) / 8 - fy0;
 1261     fx0 -= fx3 = (t2 * (t2 * ((double) (3 * xb) - t2 * (double) xa) -
 1262                 (double) (3 * xc)) + (double) xd) / 8;
 1263     fy0 -= fy3 = (t2 * (t2 * ((double) (3 * yb) - t2 * (double) ya) -
 1264                 (double) (3 * yc)) + (double) yd) / 8;
 1265     x3 = ifloor(fx3 + 0.5);
 1266     y3 = ifloor(fy3 + 0.5); /* scale bounds to int */
 1267     if (fx0 != 0.0) {
 1268         fx1 *= fx0 = (x0 - x3) / fx0;
 1269         fx2 *= fx0;
 1270     }
 1271     if (fy0 != 0.0) {
 1272         fy1 *= fy0 = (y0 - y3) / fy0;
 1273         fy2 *= fy0;
 1274     }
 1275     if (x0 != x3 || y0 != y3)   /* segment t1 - t2 */
 1276         plotCubicBezierSeg(x0, y0,
 1277                    x0 + fx1, y0 + fy1,
 1278                    x0 + fx2, y0 + fy2,
 1279                    x3, y3);
 1280     x0 = x3;
 1281     y0 = y3;
 1282     fx0 = fx3;
 1283     fy0 = fy3;
 1284     t1 = t2;
 1285     }
 1286 }
 1287 
 1288 #if 0
 1289 static void
 1290 plotQuadSpline(int n, int x[], int y[], int skip_segments)
 1291 {               /* plot quadratic spline, destroys input arrays x,y */
 1292 #define M_MAX 12
 1293     double mi = 1, m[M_MAX];    /* diagonal constants of matrix */
 1294     int i, x0, y0, x1, y1, x2, y2;
 1295 #ifdef DEBUG_SPLINE_SEGMENTS
 1296     int color = 0;
 1297 #endif
 1298 
 1299     assert(n > 1);      /* need at least 3 points P[0]..P[n] */
 1300 
 1301 #ifdef DEBUG_SPLINE_POINTS
 1302     {
 1303     int save_pattern;
 1304 
 1305     i = 0;
 1306     global_context->temporary_write_controls.foreground = 11;
 1307     save_pattern = global_context->temporary_write_controls.pattern;
 1308     global_context->temporary_write_controls.pattern = 0xff;
 1309     draw_patterned_arc(global_context, x[i], y[i], x[i] + 2, y[i], 0,
 1310                3600, NULL, NULL);
 1311     i++;
 1312     global_context->temporary_write_controls.foreground = 15;
 1313     for (; i < n; i++) {
 1314         draw_patterned_arc(global_context,
 1315                    x[i], y[i],
 1316                    x[i] + 2, y[i],
 1317                    0, 3600, NULL, NULL);
 1318     }
 1319     global_context->temporary_write_controls.foreground = 10;
 1320     draw_patterned_arc(global_context, x[i], y[n], x[i] + 2, y[i], 0,
 1321                3600, NULL, NULL);
 1322     global_context->temporary_write_controls.pattern = save_pattern;
 1323     }
 1324 #endif
 1325 
 1326     x2 = x[n];
 1327     y2 = y[n];
 1328 
 1329     x[1] = x0 = 8 * x[1] - 2 * x[0];    /* first row of matrix */
 1330     y[1] = y0 = 8 * y[1] - 2 * y[0];
 1331 
 1332     for (i = 2; i < n; i++) {   /* forward sweep */
 1333     if (i - 2 < M_MAX)
 1334         m[i - 2] = mi = 1.0 / (6.0 - mi);
 1335     x[i] = x0 = ifloor(8 * x[i] - x0 * mi + 0.5);   /* store yi */
 1336     y[i] = y0 = ifloor(8 * y[i] - y0 * mi + 0.5);
 1337     }
 1338     x1 = ifloor((x0 - 2 * x2) / (5.0 - mi) + 0.5);  /* correction last row */
 1339     y1 = ifloor((y0 - 2 * y2) / (5.0 - mi) + 0.5);
 1340 
 1341     for (i = n - 2; i > 0; i--) {   /* back substitution */
 1342     if (i <= M_MAX)
 1343         mi = m[i - 1];
 1344     x0 = ifloor((x[i] - x1) * mi + 0.5);    /* next corner */
 1345     y0 = ifloor((y[i] - y1) * mi + 0.5);
 1346 #ifdef DEBUG_SPLINE_SEGMENTS
 1347     color++;
 1348     global_context->temporary_write_controls.foreground = color;
 1349 #endif
 1350     if ((n - 2) - i < skip_segments)
 1351         plotQuadBezier((x0 + x1) / 2, (y0 + y1) / 2, x1, y1, x2, y2);
 1352     x2 = (x0 + x1) / 2;
 1353     x1 = x0;
 1354     y2 = (y0 + y1) / 2;
 1355     y1 = y0;
 1356     }
 1357 #ifdef DEBUG_SPLINE_SEGMENTS
 1358     color++;
 1359     global_context->temporary_write_controls.foreground = color;
 1360 #endif
 1361     if (skip_segments > 0)
 1362     plotQuadBezier(x[0], y[0], x1, y1, x2, y2);
 1363 }
 1364 #endif
 1365 
 1366 static void
 1367 plotCubicSpline(int n, int x[], int y[], int skip_first_last)
 1368 {
 1369 #define M_MAX 12
 1370     double mi = 0.25, m[M_MAX]; /* diagonal constants of matrix */
 1371     int x3, y3, x4, y4;
 1372     int i, x0, y0, x1, y1, x2, y2;
 1373 #ifdef DEBUG_SPLINE_SEGMENTS
 1374     RegisterNum color = 0;
 1375 #endif
 1376 
 1377     assert(n > 2);      /* need at least 4 points P[0]..P[n] */
 1378 
 1379 #ifdef DEBUG_SPLINE_POINTS
 1380     {
 1381     unsigned save_pattern;
 1382 
 1383     i = 0;
 1384     global_context->temporary_write_controls.foreground = 11;
 1385     save_pattern = global_context->temporary_write_controls.pattern;
 1386     global_context->temporary_write_controls.pattern = 0xff;
 1387     draw_patterned_arc(global_context, x[i], y[i], x[i] + 2, y[i], 0,
 1388                3600, NULL, NULL);
 1389     i++;
 1390     global_context->temporary_write_controls.foreground = 15;
 1391     for (; i < n; i++) {
 1392         draw_patterned_arc(global_context,
 1393                    x[i], y[i],
 1394                    x[i] + 2, y[i],
 1395                    0, 3600, NULL, NULL);
 1396     }
 1397     global_context->temporary_write_controls.foreground = 10;
 1398     draw_patterned_arc(global_context, x[i], y[i], x[i] + 2, y[i], 0,
 1399                3600, NULL, NULL);
 1400     global_context->temporary_write_controls.pattern = save_pattern;
 1401     }
 1402 #endif
 1403 
 1404     x3 = x[n - 1];
 1405     y3 = y[n - 1];
 1406     x4 = x[n];
 1407     y4 = y[n];
 1408 
 1409     x[1] = x0 = 12 * x[1] - 3 * x[0];   /* first row of matrix */
 1410     y[1] = y0 = 12 * y[1] - 3 * y[0];
 1411 
 1412     for (i = 2; i < n; i++) {   /* forward sweep */
 1413     if (i - 2 < M_MAX)
 1414         m[i - 2] = mi = 0.25 / (2.0 - mi);
 1415     x[i] = x0 = ifloor(12 * x[i] - 2 * x0 * mi + 0.5);
 1416     y[i] = y0 = ifloor(12 * y[i] - 2 * y0 * mi + 0.5);
 1417     }
 1418     x2 = ifloor((x0 - 3 * x4) / (7 - 4 * mi) + 0.5);    /* correct last row */
 1419     /* printf("y0=%d, y4=%d mi=%g\n", y0, y4, mi); */
 1420     y2 = ifloor((y0 - 3 * y4) / (7 - 4 * mi) + 0.5);
 1421     /* printf("y2=%d, y3=%d, y4=%d\n", y2, y3, y4); */
 1422 #ifdef DEBUG_SPLINE_SEGMENTS
 1423     color++;
 1424     global_context->temporary_write_controls.foreground = color;
 1425 #endif
 1426     if (!skip_first_last)
 1427     plotCubicBezier(x3, y3, (x2 + x4) / 2, (y2 + y4) / 2, x4, y4, x4, y4);
 1428 
 1429     if (n - 3 < M_MAX)
 1430     mi = m[n - 3];
 1431     x1 = ifloor((x[n - 2] - 2 * x2) * mi + 0.5);
 1432     y1 = ifloor((y[n - 2] - 2 * y2) * mi + 0.5);
 1433     for (i = n - 3; i > 0; i--) {   /* back substitution */
 1434     if (i <= M_MAX)
 1435         mi = m[i - 1];
 1436     x0 = ifloor((x[i] - 2 * x1) * mi + 0.5);
 1437     y0 = ifloor((y[i] - 2 * y1) * mi + 0.5);
 1438     x4 = ifloor((x0 + 4 * x1 + x2 + 3) / 6.0);  /* reconstruct P[i] */
 1439     y4 = ifloor((y0 + 4 * y1 + y2 + 3) / 6.0);
 1440 #ifdef DEBUG_SPLINE_SEGMENTS
 1441     color++;
 1442     global_context->temporary_write_controls.foreground = color;
 1443 #endif
 1444 #define CB_PARM(num) ifloor((num) / 3.0 + 0.5)
 1445     plotCubicBezier(x4, y4,
 1446             CB_PARM(2 * x1 + x2),
 1447             CB_PARM(2 * y1 + y2),
 1448             CB_PARM(x1 + 2 * x2),
 1449             CB_PARM(y1 + 2 * y2),
 1450             x3, y3);
 1451     x3 = x4;
 1452     y3 = y4;
 1453     x2 = x1;
 1454     y2 = y1;
 1455     x1 = x0;
 1456     y1 = y0;
 1457     }
 1458     x0 = x[0];
 1459     x4 = ifloor((3 * x0 + 7 * x1 + 2 * x2 + 6) / 12.0);     /* reconstruct P[1] */
 1460     y0 = y[0];
 1461     y4 = ifloor((3 * y0 + 7 * y1 + 2 * y2 + 6) / 12.0);
 1462 #ifdef DEBUG_SPLINE_SEGMENTS
 1463     global_context->temporary_write_controls.foreground = 4;
 1464 #endif
 1465     plotCubicBezier(x4, y4,
 1466             CB_PARM(2 * x1 + x2),
 1467             CB_PARM(2 * y1 + y2),
 1468             CB_PARM(x1 + 2 * x2),
 1469             CB_PARM(y1 + 2 * y2),
 1470             x3, y3);
 1471 #ifdef DEBUG_SPLINE_SEGMENTS
 1472     color++;
 1473     global_context->temporary_write_controls.foreground = color;
 1474 #endif
 1475     if (!skip_first_last)
 1476     plotCubicBezier(x0, y0, x0, y0, (x0 + x1) / 2, (y0 + y1) / 2, x4, y4);
 1477 }
 1478 
 1479 static unsigned
 1480 find_free_alphabet_index(RegisGraphicsContext *context, unsigned alphabet,
 1481              unsigned pixw, unsigned pixh)
 1482 {
 1483     unsigned ii, jj;
 1484 
 1485     /* try an exact match */
 1486     for (ii = 0U; ii < MAX_REGIS_ALPHABETS; ii++) {
 1487     if (context->alphabets[ii].alphabet_num == alphabet &&
 1488         context->alphabets[ii].pixw == pixw &&
 1489         context->alphabets[ii].pixh == pixh) {
 1490         return ii;
 1491     }
 1492     }
 1493 
 1494     /* otherwise use any empty slot */
 1495     for (ii = 0U; ii < MAX_REGIS_ALPHABETS; ii++) {
 1496     if (context->alphabets[ii].alphabet_num == INVALID_ALPHABET_NUM) {
 1497         context->alphabets[ii].alphabet_num = alphabet;
 1498         context->alphabets[ii].pixw = pixw;
 1499         context->alphabets[ii].pixh = pixh;
 1500         return ii;
 1501     }
 1502     }
 1503 
 1504     /* otherwise recycle a slot with a different font size */
 1505     for (ii = 0U; ii < MAX_REGIS_ALPHABETS; ii++) {
 1506     if (context->alphabets[ii].alphabet_num == alphabet) {
 1507         context->alphabets[ii].pixw = pixw;
 1508         context->alphabets[ii].pixh = pixh;
 1509         context->alphabets[ii].name[0] = '\0';
 1510         context->alphabets[ii].fontname[0] = '\0';
 1511         context->alphabets[ii].use_font = 0;
 1512         if (context->alphabets[ii].bytes != NULL) {
 1513         free(context->alphabets[ii].bytes);
 1514         context->alphabets[ii].bytes = NULL;
 1515         }
 1516         for (jj = 0U; jj < MAX_GLYPHS; jj++) {
 1517         context->alphabets[ii].loaded[jj] = 0;
 1518         }
 1519         return ii;
 1520     }
 1521     }
 1522 
 1523     /* finally just recycle this arbitrary slot */
 1524     context->alphabets[0U].alphabet_num = alphabet;
 1525     context->alphabets[0U].pixw = pixw;
 1526     context->alphabets[0U].pixh = pixh;
 1527     context->alphabets[0U].name[0] = '\0';
 1528     context->alphabets[0U].fontname[0] = '\0';
 1529     context->alphabets[0U].use_font = 0;
 1530     if (context->alphabets[0U].bytes != NULL) {
 1531     free(context->alphabets[0U].bytes);
 1532     context->alphabets[0U].bytes = NULL;
 1533     }
 1534     for (jj = 0U; jj < MAX_GLYPHS; jj++) {
 1535     context->alphabets[0U].loaded[jj] = 0;
 1536     }
 1537 
 1538     return 0U;
 1539 }
 1540 
 1541 #ifdef DEBUG_SPECIFIC_CHAR_METRICS
 1542 static void
 1543 dump_bitmap_pixels(Char const *pixels, unsigned w, unsigned h)
 1544 {
 1545     unsigned yy, xx;
 1546 
 1547     for (yy = 0U; yy < h; yy++) {
 1548     printf(" ");
 1549     for (xx = 0U; xx < w; xx++) {
 1550         if (pixels[yy * w + xx]) {
 1551         printf("#");
 1552         } else {
 1553         printf("_");
 1554         }
 1555     }
 1556     printf("\n");
 1557     }
 1558 }
 1559 #endif
 1560 
 1561 #if OPT_RENDERFONT && defined(HAVE_TYPE_FCCHAR32)
 1562 static int
 1563 copy_bitmap_from_xft_font(XtermWidget xw, XftFont *font, FcChar32 ch,
 1564               Char *pixels, unsigned w, unsigned h,
 1565               unsigned xmin, unsigned ymin)
 1566 {
 1567     /*
 1568      * FIXME: cache:
 1569      * - the bitmap for the last M characters and target dimensions
 1570      * - reuse the pixmap object where possible
 1571      */
 1572     Display *display = XtDisplay(xw);
 1573     Screen *screen = XtScreen(xw);
 1574     XftColor bg, fg;
 1575     Pixmap bitmap;
 1576     XftDraw *draw;
 1577     XImage *image;
 1578     GC glyph_gc;
 1579     unsigned bmw, bmh;
 1580     unsigned xx, yy;
 1581 
 1582     bmw = w + xmin;
 1583     bmh = h;
 1584     if (bmw < 1 || bmh < 1) {
 1585     TRACE(("refusing impossible bitmap size w=%d h=%d xmin=%d ymin=%d for ch='%c'\n",
 1586            bmw, bmh, xmin, ymin, ch));
 1587     return 0;
 1588     }
 1589     bitmap = XCreatePixmap(display,
 1590                DefaultRootWindow(display),
 1591                bmw, bmh, (unsigned) getVisualDepth(xw));
 1592     if (bitmap == None) {
 1593     TRACE(("unable to create Pixmap for Xft\n"));
 1594     return 0;
 1595     }
 1596     draw = XftDrawCreate(display, bitmap, xw->visInfo->visual,
 1597              XDefaultColormap(display,
 1598                       XScreenNumberOfScreen(screen)));
 1599     if (!draw) {
 1600     TRACE(("unable to create XftDraw\n"));
 1601     XFreePixmap(display, bitmap);
 1602     return 0;
 1603     }
 1604 
 1605     bg.pixel = 0UL;
 1606     bg.color.red = 0;
 1607     bg.color.green = 0;
 1608     bg.color.blue = 0;
 1609     bg.color.alpha = 0x0;
 1610     XftDrawRect(draw, &bg, 0, 0, bmw, bmh);
 1611 
 1612     fg.pixel = 1UL;
 1613     fg.color.red = 0xffff;
 1614     fg.color.green = 0xffff;
 1615     fg.color.blue = 0xffff;
 1616     fg.color.alpha = 0xffff;
 1617     XftDrawString32(draw, &fg, font, -(int) xmin, font->ascent - (int) ymin,
 1618             &ch, 1);
 1619 
 1620     glyph_gc = XCreateGC(display, bitmap, 0UL, NULL);
 1621     if (!glyph_gc) {
 1622     TRACE(("unable to create GC\n"));
 1623     XftDrawDestroy(draw);
 1624     XFreePixmap(display, bitmap);
 1625     return 0;
 1626     }
 1627     XSetForeground(display, glyph_gc, 1UL);
 1628     XSetBackground(display, glyph_gc, 0UL);
 1629     image = XGetImage(display, bitmap, 0, 0, w, h, 1UL, XYPixmap);
 1630     if (!image) {
 1631     TRACE(("unable to create XImage\n"));
 1632     XFreeGC(display, glyph_gc);
 1633     XftDrawDestroy(draw);
 1634     XFreePixmap(display, bitmap);
 1635     return 0;
 1636     }
 1637 
 1638     for (yy = 0U; yy < h; yy++) {
 1639 #ifdef DEBUG_XFT_GLYPH_COPY
 1640     TRACE(("'%c'[%02u]:", ch, yy));
 1641 #endif
 1642     for (xx = 0U; xx < w; xx++) {
 1643         unsigned long pix;
 1644         pix = XGetPixel(image, (int) xx, (int) yy);
 1645         pixels[yy * w + xx] = (unsigned char) pix;
 1646 #ifdef DEBUG_XFT_GLYPH_COPY
 1647         TRACE((" %lu", pix));
 1648 #endif
 1649     }
 1650 #ifdef DEBUG_XFT_GLYPH_COPY
 1651     TRACE(("\n"));
 1652 #endif
 1653     }
 1654 
 1655     XFreeGC(display, glyph_gc);
 1656     XDestroyImage(image);
 1657     XftDrawDestroy(draw);
 1658     XFreePixmap(display, bitmap);
 1659     return 1;
 1660 }
 1661 
 1662 static void
 1663 get_xft_glyph_dimensions(XtermWidget xw, XftFont *font, unsigned *w,
 1664              unsigned *h, unsigned *xmin, unsigned *ymin)
 1665 {
 1666     unsigned workw, workh;
 1667     FcChar32 ch;
 1668     Char *pixels;
 1669     Char *pixelp;
 1670     unsigned yy, xx;
 1671     unsigned char_count, pixel_count;
 1672     unsigned real_minx, real_maxx, real_miny, real_maxy;
 1673     unsigned char_minx, char_maxx, char_miny, char_maxy;
 1674 
 1675     /*
 1676      * For each ASCII or ISO-8859-1 printable code, find out what its
 1677      * dimensions are.
 1678      *
 1679      * We actually render the glyphs and determine the extents ourselves
 1680      * because the font library can lie by several pixels, and since we are
 1681      * doing manual character placement in fixed areas the glyph boundary needs
 1682      * to be accurate.
 1683      *
 1684      * Ignore control characters and spaces - their extent information is
 1685      * misleading.
 1686      */
 1687 
 1688     /* Our "work area" is just a buffer which should be big enough to hold the
 1689      * largest glyph even if its size is under-reported by a couple of pixels
 1690      * in each dimension.
 1691      */
 1692     workw = (unsigned) font->max_advance_width + 2U;
 1693     if (font->ascent + font->descent > font->height) {
 1694     workh = (unsigned) (font->ascent + font->descent) + 2U;
 1695     } else {
 1696     workh = (unsigned) font->height + 2U;
 1697     }
 1698 
 1699     if (!(pixels = TypeMallocN(Char, (size_t) (workw * workh)))) {
 1700     *w = 0U;
 1701     *h = 0U;
 1702 #ifdef DEBUG_COMPUTED_FONT_METRICS
 1703     TRACE(("reported metrics:\n"));
 1704     TRACE((" %ux%u ascent=%u descent=%u\n", font->max_advance_width,
 1705            font->height, font->ascent, font->descent));
 1706     TRACE(("computed metrics:\n"));
 1707     TRACE((" (unable to allocate pixel array)\n"));
 1708 #endif
 1709     return;
 1710     }
 1711 
 1712     /* FIXME: ch is in UCS32 -- try to support non-ASCII characters */
 1713     char_count = 0U;
 1714     real_minx = workw - 1U;
 1715     real_maxx = 0U;
 1716     real_miny = workh - 1U;
 1717     real_maxy = 0U;
 1718     for (ch = 33; ch < 256; ++ch) {
 1719     if (ch >= 127 && ch <= 160) {
 1720 #ifdef DEBUG_SPECIFIC_CHAR_METRICS
 1721         if (IS_DEBUG_CHAR(ch))
 1722         printf("char: '%c' not in interesting range; ignoring\n",
 1723                (char) ch);
 1724 #endif
 1725         continue;
 1726     }
 1727     if (!FcCharSetHasChar(font->charset, ch)) {
 1728 #ifdef DEBUG_SPECIFIC_CHAR_METRICS
 1729         if (IS_DEBUG_CHAR(ch))
 1730         printf("char: '%c' not in charset; ignoring\n", (char) ch);
 1731 #endif
 1732         continue;
 1733     }
 1734 
 1735     if (!copy_bitmap_from_xft_font(xw, font, ch, pixels,
 1736                        workw, workh, 0U, 0U)) {
 1737 #ifdef DEBUG_SPECIFIC_CHAR_METRICS
 1738         if (IS_DEBUG_CHAR(ch))
 1739         printf("char: '%c' bitmap could not be copied; ignoring\n",
 1740                (char) ch);
 1741 #endif
 1742         continue;
 1743     }
 1744 
 1745     pixel_count = 0U;
 1746     char_minx = workh - 1U;
 1747     char_maxx = 0U;
 1748     char_miny = workh - 1U;
 1749     char_maxy = 0U;
 1750     pixelp = pixels;
 1751     for (yy = 0U; yy < workh; yy++) {
 1752         for (xx = 0U; xx < workw; xx++) {
 1753         if (*pixelp++) {
 1754             if (xx < char_minx)
 1755             char_minx = xx;
 1756             else if (xx > char_maxx)
 1757             char_maxx = xx;
 1758             if (yy < char_miny)
 1759             char_miny = yy;
 1760             else if (yy > char_maxy)
 1761             char_maxy = yy;
 1762             pixel_count++;
 1763         }
 1764         }
 1765     }
 1766     if (pixel_count < 1U) {
 1767 #ifdef DEBUG_SPECIFIC_CHAR_METRICS
 1768         if (IS_DEBUG_CHAR(ch))
 1769         printf("char: '%c' has no pixels; ignoring\n", (char) ch);
 1770 #endif
 1771         continue;
 1772     }
 1773 #ifdef DEBUG_SPECIFIC_CHAR_METRICS
 1774     if (IS_DEBUG_CHAR(ch)) {
 1775         printf("char: '%c' (%d)\n", (char) ch, ch);
 1776         printf(" minx: %u\n", char_minx);
 1777         printf(" maxx: %u\n", char_maxx);
 1778         printf(" miny: %u\n", char_miny);
 1779         printf(" maxy: %u\n", char_maxy);
 1780         dump_bitmap_pixels(pixels, workw, workh);
 1781         printf("\n");
 1782     }
 1783 #endif
 1784 
 1785     if (char_minx < real_minx)
 1786         real_minx = char_minx;
 1787     if (char_maxx > real_maxx)
 1788         real_maxx = char_maxx;
 1789     if (char_miny < real_miny)
 1790         real_miny = char_miny;
 1791     if (char_maxy > real_maxy)
 1792         real_maxy = char_maxy;
 1793     char_count++;
 1794     }
 1795 
 1796     free(pixels);
 1797 
 1798     if (char_count < 1U) {
 1799 #ifdef DEBUG_COMPUTED_FONT_METRICS
 1800     TRACE(("reported metrics:\n"));
 1801     TRACE((" %ux%u ascent=%u descent=%u\n", font->max_advance_width,
 1802            font->height, font->ascent, font->descent));
 1803     TRACE(("computed metrics:\n"));
 1804     TRACE((" (no characters found)\n"));
 1805 #endif
 1806     *w = 0U;
 1807     *h = 0U;
 1808     return;
 1809     }
 1810 
 1811     *w = (unsigned) (1 + real_maxx - real_minx);
 1812     *h = (unsigned) (1 + real_maxy - real_miny);
 1813     *xmin = real_minx;
 1814     *ymin = real_miny;
 1815 
 1816 #ifdef DEBUG_COMPUTED_FONT_METRICS
 1817     printf("reported metrics:\n");
 1818     printf(" %ux%u ascent=%u descent=%u\n", font->max_advance_width,
 1819        font->height, font->ascent, font->descent);
 1820     printf("computed metrics:\n");
 1821     printf(" real_minx=%u real_maxx=%u real_miny=%u real_maxy=%u\n",
 1822        real_minx, real_maxx, real_miny, real_maxy);
 1823     printf(" final: %ux%u xmin=%u ymin=%u\n", *w, *h, *xmin, *ymin);
 1824 #endif
 1825 }
 1826 
 1827 #define FONT_SIZE_CACHE_SIZE 32U
 1828 
 1829 /* Find the font pixel size which returns the font which is closest to the given
 1830  * maxw and maxh without overstepping either dimension.
 1831  */
 1832 static XftFont *
 1833 find_best_xft_font_size(XtermWidget xw,
 1834             char const *fontname,
 1835             unsigned maxw, unsigned maxh, unsigned max_pixels,
 1836             unsigned *w, unsigned *h,
 1837             unsigned *xmin, unsigned *ymin)
 1838 {
 1839     Display *display = XtDisplay(xw);
 1840     Screen *screen = XtScreen(xw);
 1841     XftFont *font;
 1842     unsigned targeth;
 1843     unsigned ii, cacheindex;
 1844     /* FIXME: change cache to just cache the final result and put it in a
 1845      * wrapper function
 1846      */
 1847     static struct {
 1848     char fontname[REGIS_FONTNAME_LEN];
 1849     unsigned maxw, maxh, max_pixels;
 1850     unsigned targeth;
 1851     unsigned w, h;
 1852     unsigned xmin;
 1853     unsigned ymin;
 1854     } cache[FONT_SIZE_CACHE_SIZE];
 1855 
 1856     assert(display);
 1857     assert(screen);
 1858     assert(fontname);
 1859     assert(w);
 1860     assert(h);
 1861     assert(xmin);
 1862     assert(ymin);
 1863 
 1864 #ifdef DEBUG_FONT_SIZE_SEARCH
 1865     TRACE(("determining best size of font '%s' for %ux%u glyph with max_pixels=%u\n",
 1866        fontname, maxw, maxh, max_pixels));
 1867 #endif
 1868     cacheindex = FONT_SIZE_CACHE_SIZE;
 1869     for (ii = 0U; ii < FONT_SIZE_CACHE_SIZE; ii++) {
 1870     if (cache[ii].maxw == maxw && cache[ii].maxh == maxh &&
 1871         cache[ii].max_pixels == max_pixels &&
 1872         strcmp(cache[ii].fontname, fontname) == 0) {
 1873         cacheindex = ii;
 1874         break;
 1875     }
 1876     }
 1877 
 1878     if (cacheindex < FONT_SIZE_CACHE_SIZE) {
 1879     targeth = cache[cacheindex].targeth;
 1880     } else {
 1881     targeth = maxh * 10U + 5U;
 1882     }
 1883     for (;;) {
 1884     if (targeth <= 5U) {
 1885         TRACE(("Giving up finding suitable Xft font size for \"%s\" at %ux%u.\n",
 1886            fontname, maxw, maxh));
 1887         return NULL;
 1888     }
 1889 
 1890     /*
 1891      * Xft does a bad job at:
 1892      *  - two-color low-resolution anti-aliased fonts
 1893      *  - non-anti-aliased fonts at low resolution unless a font size is
 1894      *    given (pixel size does not help, and the value of the font size
 1895      *    doesn't appear to matter).
 1896      *
 1897      * In those two cases it literally drops pixels, sometimes whole
 1898      * columns, making the glyphs unreadable and at least ugly even when
 1899      * readable.
 1900      */
 1901     font = NULL;
 1902     /*
 1903      * FIXME:
 1904      * Also, we need to scale the width and height separately.  The
 1905      * CHAR_WIDTH and CHAR_HEIGHT attributes would seem to be ideal, but
 1906      * don't appear to have any effect if set.  Instead we will manually
 1907      * scale the bitmap later, which may be very ugly because we won't try
 1908      * to identify different parts of glyphs or preserve density.
 1909      */
 1910     {
 1911         XftPattern *pat;
 1912         XftPattern *match;
 1913         XftResult status;
 1914 
 1915         if ((pat = XftNameParse(fontname))) {
 1916 #ifdef DEBUG_FONT_SIZE_SEARCH
 1917         TRACE(("trying targeth=%g\n", targeth / 10.0));
 1918 #endif
 1919         XftPatternBuild(pat,
 1920 #if 0
 1921         /* arbitrary value */
 1922                 XFT_SIZE, XftTypeDouble, 12.0,
 1923 #endif
 1924                 XFT_PIXEL_SIZE, XftTypeDouble, (double)
 1925                 targeth / 10.0,
 1926 #if 0
 1927                 XFT_CHAR_WIDTH, XftTypeInteger, (int) maxw,
 1928                 XFT_CHAR_HEIGHT, XftTypeInteger, (int)
 1929                 (targeth / 10U),
 1930 #endif
 1931                 XFT_SPACING, XftTypeInteger, XFT_MONO,
 1932                 XFT_SLANT, XftTypeInteger, 0,
 1933                 XFT_ANTIALIAS, XftTypeBool, False,
 1934                 NULL);
 1935         if ((match = XftFontMatch(display,
 1936                       XScreenNumberOfScreen(screen),
 1937                       pat, &status))) {
 1938             font = XftFontOpenPattern(display, match);
 1939             maybeXftCache(xw, font);
 1940         }
 1941         XftPatternDestroy(pat);
 1942         }
 1943     }
 1944     if (!font) {
 1945 #ifdef DEBUG_FONT_SIZE_SEARCH
 1946         {
 1947         char buffer[1024];
 1948 
 1949         if (XftNameUnparse(font->pattern, buffer, (int) sizeof(buffer)))
 1950             printf("font name unparsed: \"%s\"\n", buffer);
 1951         }
 1952 #endif
 1953         TRACE(("unable to open a monospaced Xft font matching '%s' with pixelsize %g\n",
 1954            fontname, targeth / 10.0));
 1955         return NULL;
 1956     }
 1957 #ifdef DEBUG_FONT_SIZE_SEARCH
 1958     {
 1959         char buffer[1024];
 1960 
 1961         if (XftNameUnparse(font->pattern, buffer, (int) sizeof(buffer))) {
 1962         TRACE(("Testing font named \"%s\"\n", buffer));
 1963         } else {
 1964         TRACE(("Testing unknown font\n"));
 1965         }
 1966     }
 1967 #endif
 1968 
 1969     if (cacheindex < FONT_SIZE_CACHE_SIZE &&
 1970         targeth == cache[cacheindex].targeth) {
 1971         *w = cache[cacheindex].w;
 1972         *h = cache[cacheindex].h;
 1973         *xmin = cache[cacheindex].xmin;
 1974         *ymin = cache[cacheindex].ymin;
 1975     } else {
 1976         get_xft_glyph_dimensions(xw, font, w, h, xmin, ymin);
 1977 
 1978         if (*w < 1 || *h < 1) {
 1979 #ifdef DEBUG_FONT_SIZE_SEARCH
 1980         TRACE(("got %ux%u dimensions for target size targeth=%d; trying reduced target size\n",
 1981                *w, *h, targeth));
 1982 #endif
 1983         targeth--;
 1984         continue;
 1985         }
 1986     }
 1987 #ifdef DEBUG_FONT_SIZE_SEARCH
 1988     TRACE(("checking max=%ux%u targeth=%u.%u\n", maxw, maxh, targeth /
 1989            10U, targeth % 10U));
 1990 #endif
 1991 
 1992     if (*h > maxh) {
 1993         XftFontClose(display, font);
 1994 #ifdef DEBUG_FONT_SIZE_SEARCH
 1995         TRACE(("got %ux%u glyph; too tall; reducing target size\n", *w, *h));
 1996 #endif
 1997         if (*h > 2U * maxh) {
 1998         targeth /= (*h / maxh);
 1999         } else if (targeth > 10U && *h > maxh + 1U) {
 2000         targeth -= 10U;
 2001         } else {
 2002         targeth--;
 2003         }
 2004         continue;
 2005     }
 2006     if (*w > maxw) {
 2007         XftFontClose(display, font);
 2008 #ifdef DEBUG_FONT_SIZE_SEARCH
 2009         TRACE(("got %ux%u glyph; too wide; reducing target size\n", *w, *h));
 2010 #endif
 2011         if (*w > 2U * maxw) {
 2012         targeth /= (*w / maxw);
 2013         } else if (targeth > 10U && *w > maxw + 1U) {
 2014         targeth -= 10U;
 2015         } else {
 2016         targeth--;
 2017         }
 2018         continue;
 2019     }
 2020     if (*w * *h > max_pixels) {
 2021         XftFontClose(display, font);
 2022 #ifdef DEBUG_FONT_SIZE_SEARCH
 2023         TRACE(("got %ux%u glyph; too many pixels; reducing target size\n",
 2024            *w, *h));
 2025 #endif
 2026         if (*w * *h > 2U * max_pixels) {
 2027         unsigned min = *w < *h ? *w : *h;
 2028         unsigned divisor = (*w * *h) / (max_pixels * min);
 2029         if (divisor > 1U) {
 2030             targeth /= divisor;
 2031         } else if (targeth > 10U) {
 2032             targeth -= 10U;
 2033         } else {
 2034             targeth--;
 2035         }
 2036         } else {
 2037         targeth--;
 2038         }
 2039         continue;
 2040     }
 2041 #ifdef DEBUG_FONT_NAME
 2042     {
 2043         char buffer[1024];
 2044 
 2045         if (XftNameUnparse(font->pattern, buffer, (int) sizeof(buffer))) {
 2046         TRACE(("Final font for \"%s\" max %dx%d is \"%s\"\n",
 2047                fontname, maxw, maxh, buffer));
 2048         } else {
 2049         TRACE(("Final font for \"%s\" max %dx%d is unknown\n",
 2050                fontname, maxw, maxh));
 2051         }
 2052     }
 2053 #endif
 2054 
 2055     if (cacheindex == FONT_SIZE_CACHE_SIZE) {
 2056         for (ii = 0U; ii < FONT_SIZE_CACHE_SIZE; ii++) {
 2057         if (cache[ii].maxw == 0U || cache[ii].maxh == 0U ||
 2058             cache[ii].max_pixels == 0U) {
 2059             CopyFontname(cache[ii].fontname, fontname);
 2060             cache[ii].maxw = maxw;
 2061             cache[ii].maxh = maxh;
 2062             cache[ii].max_pixels = max_pixels;
 2063             cache[ii].targeth = targeth;
 2064             cache[ii].w = *w;
 2065             cache[ii].h = *h;
 2066             cache[ii].xmin = *xmin;
 2067             cache[ii].ymin = *ymin;
 2068             break;
 2069         }
 2070         }
 2071         if (ii == FONT_SIZE_CACHE_SIZE) {
 2072         ii = targeth % FONT_SIZE_CACHE_SIZE;
 2073         CopyFontname(cache[ii].fontname, fontname);
 2074         cache[ii].maxw = maxw;
 2075         cache[ii].maxh = maxh;
 2076         cache[ii].max_pixels = max_pixels;
 2077         cache[ii].targeth = targeth;
 2078         cache[ii].w = *w;
 2079         cache[ii].h = *h;
 2080         cache[ii].xmin = *xmin;
 2081         cache[ii].ymin = *ymin;
 2082         }
 2083     }
 2084     return font;
 2085     }
 2086 }
 2087 #endif
 2088 
 2089 static int
 2090 get_xft_bitmap_of_character(RegisGraphicsContext const *context,
 2091                 char const *fontname, int ch,
 2092                 unsigned maxw, unsigned maxh, Char *pixels,
 2093                 unsigned max_pixels, unsigned *w, unsigned *h)
 2094 {
 2095     /*
 2096      * See Xft / RENDERFONT stuff in fontutils.c and used in utils.c
 2097      * Add a separate configuration for ReGIS.
 2098      */
 2099     /*
 2100      * FIXME: cache:
 2101      * - reuse the font where possible
 2102      */
 2103 #ifdef XRENDERFONT
 2104     XtermWidget xw = context->destination_graphic->xw;
 2105     Display *display = XtDisplay(xw);
 2106     XftFont *font;
 2107     unsigned xmin = 0U, ymin = 0U;
 2108 
 2109 # ifdef DEBUG_XFT_GLYPH_LOADING
 2110     TRACE(("trying to load glyph '%c' at max size %dx%d\n", ch, maxw, maxh));
 2111 # endif
 2112     if (!(font = find_best_xft_font_size(xw, fontname, maxw, maxh,
 2113                      max_pixels, w, h, &xmin, &ymin))) {
 2114     TRACE(("Unable to find suitable Xft font\n"));
 2115     return 0;
 2116     }
 2117 
 2118     if (*w == 0U || *h == 0U) {
 2119     TRACE(("empty glyph found for '%c'\n", ch));
 2120     XftFontClose(display, font);
 2121     return 1;
 2122     }
 2123 
 2124     if (!copy_bitmap_from_xft_font(xw, font, CharOf(ch), pixels, *w, *h,
 2125                    xmin, ymin)) {
 2126     TRACE(("Unable to create bitmap for '%c'\n", ch));
 2127     XftFontClose(display, font);
 2128     return 0;
 2129     }
 2130     XftFontClose(display, font);
 2131 # ifdef DEBUG_XFT_GLYPH_LOADING
 2132     TRACE(("loaded glyph '%c' at max size %dx%d\n", ch, maxw, maxh));
 2133 # endif
 2134 
 2135     return 1;
 2136 #else
 2137     (void) context;
 2138     (void) fontname;
 2139     (void) ch;
 2140     (void) maxw;
 2141     (void) maxh;
 2142     (void) pixels;
 2143     (void) max_pixels;
 2144     (void) w;
 2145     (void) h;
 2146 
 2147     TRACE(("Not rendering Xft font for ReGIS (support not compiled in).\n"));
 2148     return 0;
 2149 #endif
 2150 }
 2151 
 2152 static unsigned
 2153 find_best_alphabet_index(RegisGraphicsContext const *context,
 2154              unsigned minw, unsigned minh,
 2155              unsigned targetw, unsigned targeth,
 2156              unsigned max_pixels)
 2157 {
 2158     unsigned ii;
 2159     unsigned bestmatch;
 2160     unsigned bestw, besth;
 2161 
 2162     assert(context);
 2163     assert(targetw);
 2164     assert(targeth);
 2165     assert(max_pixels);
 2166 
 2167     bestmatch = MAX_REGIS_ALPHABETS;
 2168     bestw = 0U;
 2169     besth = 0U;
 2170     for (ii = 0U; ii < MAX_REGIS_ALPHABETS; ii++) {
 2171     if (context->alphabets[ii].alphabet_num ==
 2172         context->current_text_controls->alphabet_num &&
 2173         context->alphabets[ii].pixw >= minw &&
 2174         context->alphabets[ii].pixh >= minh &&
 2175         context->alphabets[ii].pixw <= targetw &&
 2176         context->alphabets[ii].pixh <= targeth &&
 2177         ((context->alphabets[ii].pixw >= bestw &&
 2178           context->alphabets[ii].pixh > besth) ||
 2179          (context->alphabets[ii].pixw > bestw &&
 2180           context->alphabets[ii].pixh >= besth)) &&
 2181         context->alphabets[ii].pixw *
 2182         context->alphabets[ii].pixh <= max_pixels) {
 2183         bestmatch = ii;
 2184         bestw = context->alphabets[ii].pixw;
 2185         besth = context->alphabets[ii].pixh;
 2186     }
 2187     }
 2188 
 2189     /* If we can't find one to scale up, look for one to scale down. */
 2190     if (bestmatch == MAX_REGIS_ALPHABETS) {
 2191     bestw = max_pixels;
 2192     besth = max_pixels;
 2193     for (ii = 0U; ii < MAX_REGIS_ALPHABETS; ii++) {
 2194         if (context->alphabets[ii].alphabet_num ==
 2195         context->current_text_controls->alphabet_num &&
 2196         context->alphabets[ii].pixw >= minw &&
 2197         context->alphabets[ii].pixh >= minh &&
 2198         ((context->alphabets[ii].pixw <= bestw &&
 2199           context->alphabets[ii].pixh < besth) ||
 2200          (context->alphabets[ii].pixw < bestw &&
 2201           context->alphabets[ii].pixh <= besth)) &&
 2202         context->alphabets[ii].pixw *
 2203         context->alphabets[ii].pixh <= max_pixels) {
 2204         bestmatch = ii;
 2205         bestw = context->alphabets[ii].pixw;
 2206         besth = context->alphabets[ii].pixh;
 2207         }
 2208     }
 2209     }
 2210 #ifdef DEBUG_ALPHABET_LOOKUP
 2211     if (bestmatch < MAX_REGIS_ALPHABETS) {
 2212     TRACE(("for target size %ux%u alphabet %u found index %u size %ux%u font=%s\n",
 2213            targetw, targeth, context->current_text_controls->alphabet_num,
 2214            bestmatch,
 2215            bestw, besth,
 2216            context->alphabets[bestmatch].use_font ?
 2217            context->alphabets[bestmatch].fontname : "(none)"));
 2218     } else {
 2219     TRACE(("for target size %ux%u alphabet %u found no suitable alphabets\n",
 2220            targetw, targeth, context->current_text_controls->alphabet_num));
 2221     }
 2222 #endif
 2223 
 2224     return bestmatch;
 2225 }
 2226 
 2227 #define GLYPH_WIDTH_BYTES(PIXW) ( ((PIXW) + 7U) >> 3U )
 2228 
 2229 static int
 2230 get_user_bitmap_of_character(RegisGraphicsContext const *context,
 2231                  int ch,
 2232                  unsigned alphabet_index,
 2233                  Char *pixels,
 2234                  unsigned int max_pixels)
 2235 {
 2236     const Char *glyph;
 2237     unsigned w, h;
 2238     unsigned xx, yy;
 2239     unsigned byte, bit;
 2240 
 2241     assert(context);
 2242     assert(pixels);
 2243 
 2244     if (!context->alphabets[alphabet_index].loaded[(Char) ch]) {
 2245     TRACE(("BUG: in alphabet %u with alphabet index %u user glyph for '%c' not loaded\n",
 2246            context->current_text_controls->alphabet_num, alphabet_index,
 2247            ch));
 2248     return 0;
 2249     }
 2250 
 2251     assert(context->alphabets[alphabet_index].bytes);
 2252 
 2253     w = context->alphabets[alphabet_index].pixw;
 2254     h = context->alphabets[alphabet_index].pixh;
 2255     glyph = &context->alphabets[alphabet_index]
 2256     .bytes[(Char) ch * GLYPH_WIDTH_BYTES(w) * h];
 2257 
 2258     if (w * h > max_pixels) {
 2259     TRACE(("in alphabet %u with alphabet index %u user glyph for '%c' is too large: %ux%u (max_pixels=%u)\n",
 2260            context->current_text_controls->alphabet_num, alphabet_index,
 2261            ch, w, h, max_pixels));
 2262     return 0;
 2263     }
 2264 
 2265     for (yy = 0U; yy < h; yy++) {
 2266     for (xx = 0U; xx < w; xx++) {
 2267         byte = yy * GLYPH_WIDTH_BYTES(w) + (xx >> 3U);
 2268         bit = xx & 7U;
 2269         pixels[yy * w + xx] = (Char) (((unsigned) glyph[byte]
 2270                        >> (7U - bit)) & 1U);
 2271     }
 2272     }
 2273 
 2274     return 1;
 2275 }
 2276 
 2277 /*
 2278  * alphabets
 2279  *  0    built-in
 2280  *  1-N  custom (max is 3 on VT3X0 -- up to MAX_REGIS_ALPHABETS with xterm)
 2281  *
 2282  * built-in 7-bit charsets
 2283  *  (B    ASCII
 2284  *  (0    DEC special graphics
 2285  *  (>    DEC technical
 2286  *  (A    NCR British
 2287  *  (4    NCR Dutch
 2288  *  (5    NCR Finnish
 2289  *  (R    NCR French
 2290  *  (9    NCR French Canadian
 2291  *  (K    NCR German
 2292  *  (Y    NCR Italian
 2293  *  ('    NCR Norwegian/Danish
 2294  *  (!6   NCR Portuguese
 2295  *  (Z    NCR Spanish
 2296  *  (7    NCR Swedish
 2297  *  (-    NCR Swiss
 2298  *
 2299  * -@   ???
 2300  *
 2301  * built-in 8-bit charsets
 2302  *  )%5   DEC supplemental graphics
 2303  *  -A    ISO Latin-1 supplemental
 2304  *  )<    user-preferred supplemental (94 chars)
 2305  *
 2306  * defaults
 2307  *  terminal  char cell size   charsets      angle
 2308  *  VT3x0     S1               0:ASCII(94)   0 (positive)
 2309  *
 2310  */
 2311 static void
 2312 get_bitmap_of_character(RegisGraphicsContext const *context, int ch,
 2313             unsigned maxw, unsigned maxh, Char *pixels,
 2314             unsigned *w, unsigned *h, unsigned max_pixels)
 2315 {
 2316     unsigned bestmatch;
 2317     char const *fontname = NULL;
 2318 
 2319     assert(context);
 2320     assert(w);
 2321     assert(h);
 2322 
 2323 #ifdef DEBUG_GLYPH_RETRIEVAL
 2324     TRACE(("getting bitmap of glyph %d, current alphabet %d\n", ch,
 2325        context->current_text_controls->alphabet_num));
 2326 #endif
 2327 
 2328     if (maxw < 1U || maxh < 1U || max_pixels < 1U) {
 2329     *w = 0U;
 2330     *h = 0U;
 2331     return;
 2332     }
 2333 
 2334     if (context->current_text_controls->alphabet_num == 0)
 2335     fontname = context->builtin_font;
 2336 
 2337     *w = 0U;
 2338     *h = 0U;
 2339 
 2340     bestmatch = find_best_alphabet_index(context, 1U, 1U, maxw, maxh,
 2341                      max_pixels);
 2342     if (bestmatch < MAX_REGIS_ALPHABETS) {
 2343     RegisAlphabet const *alpha = &context->alphabets[bestmatch];
 2344 
 2345 #ifdef DEBUG_GLYPH_RETRIEVAL
 2346     TRACE(("checking user glyph for slot=%u alphabet=%d use_font=%d loaded=%d\n",
 2347            bestmatch, alpha->alphabet_num, alpha->use_font,
 2348            alpha->loaded[ch]));
 2349 #endif
 2350     if (!alpha->use_font &&
 2351         get_user_bitmap_of_character(context, ch, bestmatch, pixels,
 2352                      max_pixels)) {
 2353 #ifdef DEBUG_GLYPH_RETRIEVAL
 2354         TRACE(("found user glyph for alphabet number %d (index %u)\n\n",
 2355            alpha->alphabet_num, bestmatch));
 2356 #endif
 2357         *w = alpha->pixw;
 2358         *h = alpha->pixh;
 2359         return;
 2360     }
 2361 
 2362     if (alpha->use_font)
 2363         fontname = alpha->fontname;
 2364     }
 2365 
 2366     if (fontname) {
 2367 #ifdef DEBUG_GLYPH_RETRIEVAL
 2368     TRACE(("using xft font %s\n", fontname));
 2369 #endif
 2370     if (get_xft_bitmap_of_character(context, fontname, ch,
 2371                     maxw, maxh, pixels,
 2372                     max_pixels, w, h)) {
 2373         if (*w > maxw) {
 2374         TRACE(("BUG: Xft glyph is too wide: %ux%u but max is %ux%u\n",
 2375                *w, *h, maxw, maxh));
 2376         } else if (*h > maxh) {
 2377         TRACE(("BUG: Xft glyph is too tall: %ux%u but max is %ux%u\n",
 2378                *w, *h, maxw, maxh));
 2379         } else if (*w * *h > max_pixels) {
 2380         TRACE(("BUG: Xft glyph has too many pixels: %u but max is %u\n",
 2381                *w * *h, max_pixels));
 2382         } else {
 2383         TRACE(("got glyph from \"%s\" for alphabet number %d\n",
 2384                fontname, context->current_text_controls->alphabet_num));
 2385 #ifdef DEBUG_SPECIFIC_CHAR_METRICS
 2386         if (IS_DEBUG_CHAR(ch)) {
 2387             printf("got %ux%u Xft bitmap for '%c' target size %ux%u:\n",
 2388                *w, *h,
 2389                ch, maxw, maxh);
 2390             dump_bitmap_pixels(pixels, *w, *h);
 2391             printf("\n");
 2392         }
 2393 #endif
 2394         return;
 2395         }
 2396     }
 2397     }
 2398 
 2399     TRACE(("unable to load any bitmap for character '%c' in alphabet number %u at %ux%u\n",
 2400        ch, context->current_text_controls->alphabet_num, maxw, maxh));
 2401 
 2402     /*
 2403      * The VT3x0 series (and probably earlier ReGIS implementations) use a solid
 2404      * block glyph for unknown glyphs.
 2405      */
 2406     {
 2407     unsigned xx, yy;
 2408 
 2409     *w = MIN2(8U, maxh);
 2410     *h = MIN2(10U, maxw);
 2411     for (yy = 0U; yy < *h; yy++)
 2412         for (xx = 0U; xx < *w; xx++)
 2413         pixels[yy * *w + xx] = '\1';
 2414     }
 2415 }
 2416 
 2417 #define ROT_SHEAR_SCALE 8192
 2418 #define SIGNED_UNSIGNED_MOD(VAL, BASE) ( (((VAL) % (int) (BASE)) + (int) (BASE)) % (int) (BASE) )
 2419 
 2420 static unsigned
 2421 get_shade_character_pixel(Char const *pixels, unsigned w, unsigned h,
 2422               unsigned smaxf, unsigned scale, int slant_dx,
 2423               int px, int py)
 2424 {
 2425     unsigned wx, wy;
 2426     unsigned fx, fy;
 2427 
 2428     wx = (unsigned) SIGNED_UNSIGNED_MOD(px -
 2429                     (slant_dx * SIGNED_UNSIGNED_MOD(py, smaxf))
 2430                     / ROT_SHEAR_SCALE, smaxf);
 2431     wy = (unsigned) SIGNED_UNSIGNED_MOD(py, smaxf);
 2432 
 2433     fx = (wx * scale) >> SCALE_FIXED_POINT;
 2434     fy = (wy * scale) >> SCALE_FIXED_POINT;
 2435     if (fx < w && fy < h) {
 2436     return (unsigned) pixels[fy * w + fx];
 2437     }
 2438     return 0U;
 2439 }
 2440 
 2441 static void
 2442 draw_character(RegisGraphicsContext *context, int ch,
 2443            int slant_dx, int rot_shear_x,
 2444            int rot_shear_y, int x_sign_x, int x_sign_y,
 2445            int y_sign_x, int y_sign_y)
 2446 {
 2447     const unsigned xmaxd = context->current_text_controls->character_display_w;
 2448     const unsigned ymaxd = context->current_text_controls->character_display_h;
 2449     const unsigned xmaxf = context->current_text_controls->character_unit_cell_w;
 2450     const unsigned ymaxf = context->current_text_controls->character_unit_cell_h;
 2451     unsigned w, h;
 2452     unsigned xscale, yscale;
 2453     unsigned fx, fy;
 2454     unsigned px, py;
 2455     int sx;
 2456     int rx, ry;
 2457     int ox, oy;
 2458     unsigned pad_left, pad_right;
 2459     unsigned pad_top, pad_bottom;
 2460     Char pixels[MAX_GLYPH_PIXELS];
 2461     unsigned value;
 2462 
 2463     get_bitmap_of_character(context, ch, xmaxf, ymaxf, pixels, &w, &h,
 2464                 MAX_GLYPH_PIXELS);
 2465     if (w < 1 || h < 1) {
 2466     return;
 2467     }
 2468 
 2469     if (xmaxd > xmaxf) {
 2470     pad_left = (xmaxd - xmaxf) / 2U;
 2471     pad_right = (xmaxd - xmaxf) - pad_left;
 2472     } else {
 2473     pad_left = 0U;
 2474     pad_right = 0U;
 2475     }
 2476     if (ymaxd > ymaxf) {
 2477     pad_top = (ymaxd - ymaxf) / 2U;
 2478     pad_bottom = (ymaxd - ymaxf) - pad_top;
 2479     } else {
 2480     pad_top = 0U;
 2481     pad_bottom = 0U;
 2482     }
 2483 
 2484     xscale = (w << SCALE_FIXED_POINT) / xmaxf;
 2485     yscale = (h << SCALE_FIXED_POINT) / ymaxf;
 2486 
 2487     for (py = 0U; py < ymaxd; py++) {
 2488     for (px = 0U; px < xmaxd; px++) {
 2489         if (py < pad_top || px < pad_left ||
 2490         py >= ymaxd - pad_bottom || px >= xmaxd - pad_right) {
 2491         value = 0U;
 2492         } else {
 2493         fx = ((px - pad_left) * xscale) >> SCALE_FIXED_POINT;
 2494         fy = ((py - pad_top) * yscale) >> SCALE_FIXED_POINT;
 2495         if (fx < w && fy < h) {
 2496             value = (unsigned) pixels[fy * w + fx];
 2497         } else {
 2498             value = 0U;
 2499         }
 2500         }
 2501 
 2502         sx = (int) px + (slant_dx * (int) py) / ROT_SHEAR_SCALE;
 2503         rx = x_sign_x * sx + x_sign_y * (int) py;
 2504         ry = y_sign_x * sx + y_sign_y * (int) py;
 2505         ox = rx + (rot_shear_x * ry) / ROT_SHEAR_SCALE;
 2506         oy = ry + (rot_shear_y * ox) / ROT_SHEAR_SCALE;
 2507         ox += (rot_shear_x * oy) / ROT_SHEAR_SCALE;
 2508 
 2509         draw_regis_pixel(context,
 2510                  (int) context->graphics_output_cursor_x + ox,
 2511                  (int) context->graphics_output_cursor_y + oy,
 2512                  value);
 2513     }
 2514     }
 2515 }
 2516 
 2517 static void
 2518 move_text(RegisGraphicsContext *context, int dx, int dy)
 2519 {
 2520     double total_rotation;
 2521     int str_invert;
 2522     int str_shear_x, str_shear_y;
 2523     int ox, oy;
 2524 
 2525     total_rotation = 2.0 * M_PI *
 2526     context->current_text_controls->string_rotation / 360.0;
 2527     while (total_rotation > 1.5 * M_PI) {
 2528     total_rotation -= 2.0 * M_PI;
 2529     }
 2530     if (total_rotation > 0.5 * M_PI) {
 2531     total_rotation -= M_PI;
 2532     str_invert = -1;
 2533     } else {
 2534     str_invert = 1;
 2535     }
 2536     str_shear_x = (int) (ROT_SHEAR_SCALE * -tan(0.5 * -total_rotation));
 2537     str_shear_y = (int) (ROT_SHEAR_SCALE * sin(-total_rotation));
 2538 
 2539     total_rotation = 2.0 * M_PI *
 2540     context->current_text_controls->character_rotation / 360.0;
 2541     while (total_rotation > 1.5 * M_PI) {
 2542     total_rotation -= 2.0 * M_PI;
 2543     }
 2544 
 2545     TRACE(("str_shear: %.5f, %.5f (sign=%d)\n",
 2546        str_shear_x / (double) ROT_SHEAR_SCALE,
 2547        str_shear_y / (double) ROT_SHEAR_SCALE,
 2548        str_invert));
 2549 
 2550     ox = str_invert * dx + (str_shear_x * dy) / ROT_SHEAR_SCALE;
 2551     oy = str_invert * dy + (str_shear_y * ox) / ROT_SHEAR_SCALE;
 2552     ox += (str_shear_x * oy) / ROT_SHEAR_SCALE;
 2553 
 2554     TRACE(("after pv output updating position %+d,%+d\n", ox, oy));
 2555     context->graphics_output_cursor_x += ox;
 2556     context->graphics_output_cursor_y += oy;
 2557 
 2558     return;
 2559 }
 2560 
 2561 #define UPSCALE_TEXT_DIMENSION(D) do { \
 2562         *(D) = (unsigned)((double)(*(D)) * M_SQRT2); \
 2563     } while (0)
 2564 
 2565 static void
 2566 draw_text(RegisGraphicsContext *context, char const *str)
 2567 {
 2568 #ifndef ENABLE_DISTORTIONLESS_ROTATION
 2569     RegisTextControls *old_text_controls = NULL;
 2570     static RegisTextControls scratch_text_controls;
 2571 #endif
 2572     double total_rotation;
 2573     size_t ii;
 2574     int str_invert;
 2575     int str_shear_x, str_shear_y;
 2576     int slant_dx;
 2577     int chr_x_sign_x, chr_x_sign_y;
 2578     int chr_y_sign_x, chr_y_sign_y;
 2579     int chr_shear_x, chr_shear_y;
 2580     int begin_x, begin_y;
 2581     int rx, ry;
 2582     int ox, oy;
 2583 
 2584 #ifdef DEBUG_ALPHABETS
 2585     {
 2586     unsigned n;
 2587 
 2588     for (n = 0U; n < MAX_REGIS_ALPHABETS; n++) {
 2589         printf("alphabet index %u\n", n);
 2590         if (context->alphabets[n].alphabet_num != INVALID_ALPHABET_NUM) {
 2591         printf(" alphabet_num=%u\n", context->alphabets[n].alphabet_num);
 2592         printf(" pixw=%d\n", context->alphabets[n].pixw);
 2593         printf(" pixh=%d\n", context->alphabets[n].pixh);
 2594         printf(" name=\"%s\"\n", context->alphabets[n].name);
 2595         printf(" use_font=%d\n", context->alphabets[n].use_font);
 2596         printf(" fontname=\"%s\"\n", context->alphabets[n].fontname);
 2597         printf(" bytes=%p\n", context->alphabets[n].bytes);
 2598         }
 2599     }
 2600     }
 2601 #endif
 2602 
 2603     if (context->current_text_controls->slant <= -75 ||
 2604     context->current_text_controls->slant >= +75) {
 2605     TRACE(("ERROR: unsupported character slant angle %d\n",
 2606            context->current_text_controls->slant));
 2607     return;
 2608     }
 2609 
 2610     /* FIXME: grab when first entering command */
 2611     begin_x = context->graphics_output_cursor_x;
 2612     begin_y = context->graphics_output_cursor_y;
 2613 
 2614 #ifndef ENABLE_DISTORTIONLESS_ROTATION
 2615     if (context->current_text_controls->character_rotation != 0 &&
 2616     context->current_text_controls->character_rotation != 90 &&
 2617     context->current_text_controls->character_rotation != 180 &&
 2618     context->current_text_controls->character_rotation != 270) {
 2619     old_text_controls = context->current_text_controls;
 2620     scratch_text_controls = *context->current_text_controls;
 2621     UPSCALE_TEXT_DIMENSION(&scratch_text_controls.character_display_w);
 2622     UPSCALE_TEXT_DIMENSION(&scratch_text_controls.character_display_h);
 2623     /* FIXME: Not sure if this is really scaled.  The increment seems to
 2624      * _not_ be scaled.
 2625      */
 2626     UPSCALE_TEXT_DIMENSION(&scratch_text_controls.character_unit_cell_w);
 2627     UPSCALE_TEXT_DIMENSION(&scratch_text_controls.character_unit_cell_h);
 2628     context->current_text_controls = &scratch_text_controls;
 2629     TRACE(("scaled up text to %dx%d\n",
 2630            scratch_text_controls.character_display_w,
 2631            scratch_text_controls.character_display_h));
 2632     }
 2633 #endif
 2634 
 2635     total_rotation = 2.0 * M_PI *
 2636     context->current_text_controls->string_rotation / 360.0;
 2637     while (total_rotation > 1.5 * M_PI) {
 2638     total_rotation -= 2.0 * M_PI;
 2639     }
 2640     if (total_rotation > 0.5 * M_PI) {
 2641     total_rotation -= M_PI;
 2642     str_invert = -1;
 2643     } else {
 2644     str_invert = 1;
 2645     }
 2646     str_shear_x = (int) (ROT_SHEAR_SCALE * -tan(0.5 * -total_rotation));
 2647     str_shear_y = (int) (ROT_SHEAR_SCALE * sin(-total_rotation));
 2648 
 2649     total_rotation = 2.0 * M_PI *
 2650     context->current_text_controls->character_rotation / 360.0;
 2651     while (total_rotation > 1.5 * M_PI) {
 2652     total_rotation -= 2.0 * M_PI;
 2653     }
 2654     if (total_rotation > 0.5 * M_PI) {
 2655     total_rotation -= M_PI;
 2656     chr_x_sign_x = -1;
 2657     chr_x_sign_y = 0;
 2658     chr_y_sign_x = 0;
 2659     chr_y_sign_y = -1;
 2660     } else {
 2661     chr_x_sign_x = 1;
 2662     chr_x_sign_y = 0;
 2663     chr_y_sign_x = 0;
 2664     chr_y_sign_y = 1;
 2665     }
 2666     chr_shear_x = (int) (ROT_SHEAR_SCALE * -tan(0.5 * -total_rotation));
 2667     chr_shear_y = (int) (ROT_SHEAR_SCALE * sin(-total_rotation));
 2668 
 2669     {
 2670     const int slant = context->current_text_controls->slant;
 2671 
 2672     TRACE(("float version: %.5f\n", tan(2.0 * M_PI * abs(slant) / 360.0)));
 2673     /* The slant is negative for forward-leaning characters. */
 2674     if (slant > 0) {
 2675         slant_dx = (int) +(tan(2.0 * M_PI * abs(slant) / 360.0) * ROT_SHEAR_SCALE);
 2676     } else if (slant < 0) {
 2677         slant_dx = (int) -(tan(2.0 * M_PI * abs(slant) / 360.0) * ROT_SHEAR_SCALE);
 2678     } else {
 2679         slant_dx = 0;
 2680     }
 2681     TRACE(("string rotation: %d\n",
 2682            context->current_text_controls->string_rotation));
 2683     TRACE(("character rotation: %d\n",
 2684            context->current_text_controls->character_rotation));
 2685     TRACE(("character slant: %d (%.5f pixels per line)\n",
 2686            slant, slant_dx / (double) ROT_SHEAR_SCALE));
 2687     }
 2688 
 2689     TRACE(("str_shear: %.5f, %.5f (sign=%d)\n",
 2690        str_shear_x / (double) ROT_SHEAR_SCALE,
 2691        str_shear_y / (double) ROT_SHEAR_SCALE,
 2692        str_invert));
 2693     TRACE(("chr_shear: %.5f, %.5f (xsign=%d,%d, ysign=%d,%d)\n",
 2694        chr_shear_x / (double) ROT_SHEAR_SCALE,
 2695        chr_shear_y / (double) ROT_SHEAR_SCALE,
 2696        chr_x_sign_x, chr_x_sign_y,
 2697        chr_y_sign_x, chr_y_sign_y));
 2698     TRACE(("character_inc: %d,%d\n",
 2699        context->current_text_controls->character_inc_x, context->current_text_controls->character_inc_y));
 2700 
 2701     rx = 0;
 2702     ry = 0;
 2703     for (ii = 0U; ii < strlen(str); ii++) {
 2704     switch (str[ii]) {
 2705     case '\r':
 2706         rx = 0;
 2707         break;
 2708     case '\n':
 2709         ry += (int) context->current_text_controls->character_display_h;
 2710         break;
 2711     case '\b':
 2712         rx -= context->current_text_controls->character_inc_x;
 2713         ry -= context->current_text_controls->character_inc_y;
 2714         break;
 2715     case '\t':
 2716         rx += context->current_text_controls->character_inc_x;
 2717         ry += context->current_text_controls->character_inc_y;
 2718         break;
 2719     default:
 2720         ox = str_invert * rx + (str_shear_x * ry) / ROT_SHEAR_SCALE;
 2721         oy = str_invert * ry + (str_shear_y * ox) / ROT_SHEAR_SCALE;
 2722         ox += (str_shear_x * oy) / ROT_SHEAR_SCALE;
 2723         TRACE(("during text output updating position to %d,%d + %+d,%+d for '%c'\n",
 2724            begin_x, begin_y, ox, oy, str[ii]));
 2725         context->graphics_output_cursor_x = begin_x + ox;
 2726         context->graphics_output_cursor_y = begin_y + oy;
 2727         draw_character(context, str[ii], slant_dx,
 2728                chr_shear_x, chr_shear_y,
 2729                chr_x_sign_x, chr_x_sign_y,
 2730                chr_y_sign_x, chr_y_sign_y);
 2731         rx += context->current_text_controls->character_inc_x;
 2732         ry += context->current_text_controls->character_inc_y;
 2733     }
 2734     }
 2735 
 2736     ox = str_invert * rx + (str_shear_x * ry) / ROT_SHEAR_SCALE;
 2737     oy = str_invert * ry + (str_shear_y * ox) / ROT_SHEAR_SCALE;
 2738     ox += (str_shear_x * oy) / ROT_SHEAR_SCALE;
 2739     TRACE(("after text output updating position to %d,%d + %+d,%+d\n",
 2740        begin_x, begin_y, ox, oy));
 2741     context->graphics_output_cursor_x = begin_x + ox;
 2742     context->graphics_output_cursor_y = begin_y + oy;
 2743 
 2744 #ifndef ENABLE_DISTORTIONLESS_ROTATION
 2745     if (context->current_text_controls->character_rotation != 0 &&
 2746     context->current_text_controls->character_rotation != 90 &&
 2747     context->current_text_controls->character_rotation != 180 &&
 2748     context->current_text_controls->character_rotation != 270) {
 2749     context->current_text_controls = old_text_controls;
 2750     }
 2751 #endif
 2752 
 2753     context->destination_graphic->dirty = True;
 2754     return;
 2755 }
 2756 
 2757 /*
 2758  * standard character cell sizes
 2759  *   number  disp cell   unit cell       offset
 2760  *   S0      [  9, 10]   [  8, disp_h]   [disp_w, 0]
 2761  *   S1      [  9, 20]   [  8, disp_h]   [disp_w, 0]
 2762  *   S2      [ 18, 30]   [ 16, disp_h]   [disp_w, 0]
 2763  *   S3      [ 27, 45]   [ 24, disp_h]   [disp_w, 0]
 2764  *   S4      [ 36, 60]   [ 32, disp_h]   [disp_w, 0]
 2765  *   S5      [ 45, 75]   [ 40, disp_h]   [disp_w, 0]
 2766  *   S6      [ 54, 90]   [ 48, disp_h]   [disp_w, 0]
 2767  *   S7      [ 63,105]   [ 56, disp_h]   [disp_w, 0]
 2768  *   S8      [ 72,120]   [ 64, disp_h]   [disp_w, 0]
 2769  *   S9      [ 81,135]   [ 72, disp_h]   [disp_w, 0]
 2770  *   S10     [ 90,150]   [ 80, disp_h]   [disp_w, 0]
 2771  *   S11     [ 99,165]   [ 88, disp_h]   [disp_w, 0]
 2772  *   S12     [108,180]   [ 96, disp_h]   [disp_w, 0]
 2773  *   S13     [117,195]   [104, disp_h]   [disp_w, 0]
 2774  *   S14     [126,210]   [112, disp_h]   [disp_w, 0]
 2775  *   S15     [135,225]   [120, disp_h]   [disp_w, 0]
 2776  *   S16     [144,240]   [128, disp_h]   [disp_w, 0]
 2777  */
 2778 static int
 2779 get_standard_character_size(int standard, unsigned *disp_w, unsigned
 2780                 *disp_h, unsigned *unit_w, unsigned *unit_h,
 2781                 int *off_x, int *off_y)
 2782 {
 2783     switch (standard) {
 2784     case 0:
 2785     *disp_w = 9U;
 2786     *disp_h = 10U;
 2787     *unit_w = 8U;
 2788     break;
 2789     case 1:
 2790     *disp_w = 9U;
 2791     *disp_h = 20U;
 2792     *unit_w = 8U;
 2793     break;
 2794     case 2:
 2795     *disp_w = 18U;
 2796     *disp_h = 30U;
 2797     *unit_w = 16U;
 2798     break;
 2799     case 3:
 2800     *disp_w = 27U;
 2801     *disp_h = 45U;
 2802     *unit_w = 24U;
 2803     break;
 2804     case 4:
 2805     *disp_w = 36U;
 2806     *disp_h = 60U;
 2807     *unit_w = 32U;
 2808     break;
 2809     case 5:
 2810     *disp_w = 45U;
 2811     *disp_h = 75U;
 2812     *unit_w = 40U;
 2813     break;
 2814     case 6:
 2815     *disp_w = 54U;
 2816     *disp_h = 90U;
 2817     *unit_w = 48U;
 2818     break;
 2819     case 7:
 2820     *disp_w = 63U;
 2821     *disp_h = 105U;
 2822     *unit_w = 56U;
 2823     break;
 2824     case 8:
 2825     *disp_w = 72U;
 2826     *disp_h = 120U;
 2827     *unit_w = 64U;
 2828     break;
 2829     case 9:
 2830     *disp_w = 81U;
 2831     *disp_h = 135U;
 2832     *unit_w = 72U;
 2833     break;
 2834     case 10:
 2835     *disp_w = 90U;
 2836     *disp_h = 150U;
 2837     *unit_w = 80U;
 2838     break;
 2839     case 11:
 2840     *disp_w = 99U;
 2841     *disp_h = 165U;
 2842     *unit_w = 88U;
 2843     break;
 2844     case 12:
 2845     *disp_w = 108U;
 2846     *disp_h = 180U;
 2847     *unit_w = 96U;
 2848     break;
 2849     case 13:
 2850     *disp_w = 117U;
 2851     *disp_h = 195U;
 2852     *unit_w = 104U;
 2853     break;
 2854     case 14:
 2855     *disp_w = 126U;
 2856     *disp_h = 210U;
 2857     *unit_w = 112U;
 2858     break;
 2859     case 15:
 2860     *disp_w = 135U;
 2861     *disp_h = 225U;
 2862     *unit_w = 120U;
 2863     break;
 2864     case 16:
 2865     *disp_w = 144U;
 2866     *disp_h = 240U;
 2867     *unit_w = 128U;
 2868     break;
 2869     default:
 2870     return 1;
 2871     }
 2872     *unit_h = *disp_h;
 2873     *off_x = (int) *disp_w;
 2874     *off_y = 0;
 2875 
 2876     return 0;
 2877 }
 2878 
 2879 static void
 2880 init_fragment(RegisDataFragment *fragment, char const *str)
 2881 {
 2882     assert(fragment);
 2883     assert(str);
 2884 
 2885     fragment->start = str;
 2886     fragment->len = (unsigned) strlen(str);
 2887     fragment->pos = 0U;
 2888 }
 2889 
 2890 static void
 2891 copy_fragment(RegisDataFragment *dst, RegisDataFragment const *src)
 2892 {
 2893     assert(dst);
 2894     assert(src);
 2895 
 2896     dst->start = src->start;
 2897     dst->len = src->len;
 2898     dst->pos = src->pos;
 2899 }
 2900 
 2901 static char
 2902 peek_fragment(RegisDataFragment const *fragment)
 2903 {
 2904     assert(fragment);
 2905 
 2906     if (fragment->pos < fragment->len) {
 2907     return fragment->start[fragment->pos];
 2908     }
 2909     return '\0';
 2910 }
 2911 
 2912 static char
 2913 pop_fragment(RegisDataFragment *fragment)
 2914 {
 2915     assert(fragment);
 2916 
 2917     if (fragment->pos < fragment->len) {
 2918     return fragment->start[fragment->pos++];
 2919     }
 2920     return '\0';
 2921 }
 2922 
 2923 static char
 2924 get_fragment(RegisDataFragment const *fragment, unsigned pos)
 2925 {
 2926     assert(fragment);
 2927 
 2928     if (fragment->pos + pos < fragment->len) {
 2929     return fragment->start[fragment->pos + pos];
 2930     }
 2931     return '\0';
 2932 }
 2933 
 2934 #define fragment_length(f) (f)->len
 2935 
 2936 static unsigned
 2937 fragment_remaining(RegisDataFragment const *fragment)
 2938 {
 2939     assert(fragment);
 2940 
 2941     if (fragment->pos > fragment->len)
 2942     return 0U;
 2943     return fragment->len - fragment->pos;
 2944 }
 2945 
 2946 static int
 2947 fragment_consumed(RegisDataFragment const *fragment)
 2948 {
 2949     assert(fragment);
 2950 
 2951     return fragment->pos >= fragment->len;
 2952 }
 2953 
 2954 static void
 2955 fragment_to_string(RegisDataFragment const *fragment, char *out,
 2956            unsigned outlen)
 2957 {
 2958     unsigned remaininglen;
 2959     unsigned endpos;
 2960 
 2961     assert(fragment);
 2962     assert(out);
 2963 
 2964     if (!outlen)
 2965     return;
 2966     remaininglen = fragment->len - fragment->pos;
 2967     if (remaininglen < outlen - 1U) {
 2968     endpos = remaininglen;
 2969     } else {
 2970     endpos = outlen - 1U;
 2971     }
 2972     strncpy(out, &fragment->start[fragment->pos], (size_t) endpos);
 2973     out[endpos] = '\0';
 2974 }
 2975 
 2976 #define MAX_FRAG 1024
 2977 static char const *
 2978 fragment_to_tempstr(RegisDataFragment const *fragment)
 2979 {
 2980     static char tempstr[MAX_FRAG];
 2981 
 2982     assert(fragment);
 2983 
 2984     fragment_to_string(fragment, tempstr, MAX_FRAG);
 2985     return tempstr;
 2986 }
 2987 
 2988 static int
 2989 skip_regis_whitespace(RegisDataFragment *input)
 2990 {
 2991     int skipped = 0;
 2992 
 2993     assert(input);
 2994 
 2995     while (!fragment_consumed(input)) {
 2996     char ch = peek_fragment(input);
 2997     if (ch != ',' && !IsSpace(ch)) {
 2998         break;
 2999     }
 3000     if (ch == '\n') {
 3001         TRACE(("end of input line\n\n"));
 3002     }
 3003     skipped = 1;
 3004     pop_fragment(input);
 3005     }
 3006 
 3007     if (skipped)
 3008     return 1;
 3009     return 0;
 3010 }
 3011 
 3012 static int
 3013 extract_regis_extent(RegisDataFragment *input, RegisDataFragment *output)
 3014 {
 3015     char ch;
 3016 
 3017     assert(input);
 3018     assert(output);
 3019 
 3020     output->start = &input->start[input->pos];
 3021     output->len = 0U;
 3022     output->pos = 0U;
 3023 
 3024     if (input->pos >= input->len)
 3025     return 0;
 3026 
 3027     ch = input->start[input->pos];
 3028     if (ch != '[')
 3029     return 0;
 3030     input->pos++;
 3031     output->start++;
 3032 
 3033     /* FIXME: truncate to 16 bit signed integers */
 3034     for (; input->pos < input->len; input->pos++, output->len++) {
 3035     ch = input->start[input->pos];
 3036     if (ch == ';') {
 3037         TRACE(("DATA_ERROR: end of input before closing bracket\n"));
 3038         break;
 3039     }
 3040     if (ch == ']')
 3041         break;
 3042     }
 3043     if (ch == ']')
 3044     input->pos++;
 3045 
 3046     return 1;
 3047 }
 3048 
 3049 static int
 3050 extract_regis_num(RegisDataFragment *input, RegisDataFragment *output)
 3051 {
 3052     char ch = 0;
 3053     int has_digits = 0;
 3054 
 3055     assert(input);
 3056     assert(output);
 3057 
 3058     output->start = &input->start[input->pos];
 3059     output->len = 0U;
 3060     output->pos = 0U;
 3061 
 3062     if (input->start[input->pos] == '-' ||
 3063     input->start[input->pos] == '+') {
 3064     input->pos++;
 3065     output->len++;
 3066     }
 3067 
 3068     for (; input->pos < input->len; input->pos++, output->len++) {
 3069     ch = input->start[input->pos];
 3070     if (ch != '0' && ch != '1' && ch != '2' && ch != '3' &&
 3071         ch != '4' && ch != '5' && ch != '6' && ch != '7' &&
 3072         ch != '8' && ch != '9') {
 3073         break;
 3074     }
 3075     has_digits = 1;
 3076     }
 3077 
 3078     /* FIXME: what degenerate forms should be accepted ("E10" "1E" "1e" "1." "1ee10")? */
 3079     /* FIXME: the terminal is said to support "floating point values", truncating to int... what do these look like? */
 3080     if (has_digits && ch == 'E') {
 3081     input->pos++;
 3082     output->len++;
 3083     for (; input->pos < input->len; input->pos++, output->len++) {
 3084         ch = input->start[input->pos];
 3085         if (ch != '0' && ch != '1' && ch != '2' && ch != '3' &&
 3086         ch != '4' && ch != '5' && ch != '6' && ch != '7' &&
 3087         ch != '8' && ch != '9') {
 3088         break;
 3089         }
 3090     }
 3091     }
 3092 
 3093     return has_digits;
 3094 }
 3095 
 3096 static int
 3097 extract_regis_pixelvector(RegisDataFragment *input, RegisDataFragment *output)
 3098 {
 3099     char ch;
 3100     int has_digits;
 3101 
 3102     assert(input);
 3103     assert(output);
 3104 
 3105     output->start = &input->start[input->pos];
 3106     output->len = 0U;
 3107     output->pos = 0U;
 3108 
 3109     if (input->pos < input->len) {
 3110     ch = input->start[input->pos];
 3111     if (ch == '+' || ch == '-') {
 3112         input->pos++;
 3113         output->len++;
 3114     }
 3115     }
 3116 
 3117     has_digits = 0;
 3118     for (; input->pos < input->len; input->pos++, output->len++) {
 3119     ch = input->start[input->pos];
 3120     if (ch != '0' && ch != '1' && ch != '2' && ch != '3' &&
 3121         ch != '4' && ch != '5' && ch != '6' && ch != '7') {
 3122         break;
 3123     }
 3124     has_digits = 1;
 3125     }
 3126 
 3127     return has_digits;
 3128 }
 3129 
 3130 static int
 3131 extract_regis_command(RegisDataFragment *input, char *command)
 3132 {
 3133     char ch;
 3134 
 3135     assert(input);
 3136     assert(command);
 3137 
 3138     if (input->pos >= input->len)
 3139     return 0;
 3140 
 3141     ch = input->start[input->pos];
 3142     if (ch == '\0' || ch == ';') {
 3143     return 0;
 3144     }
 3145     if (!islower(CharOf(ch)) && !isupper(CharOf(ch)) && ch != '@') {
 3146     return 0;
 3147     }
 3148     *command = ch;
 3149     input->pos++;
 3150 
 3151     return 1;
 3152 }
 3153 
 3154 static int
 3155 extract_regis_string(RegisDataFragment *input, char *out, unsigned maxlen)
 3156 {
 3157     char open_quote_ch;
 3158     char ch;
 3159     unsigned outlen;
 3160 
 3161     assert(input);
 3162     assert(out);
 3163     assert(maxlen > 0U);
 3164 
 3165     if (input->pos >= input->len)
 3166     return 0;
 3167 
 3168     ch = peek_fragment(input);
 3169     if (ch != '\'' && ch != '"')
 3170     return 0;
 3171     open_quote_ch = ch;
 3172     outlen = 0U;
 3173     pop_fragment(input);
 3174 
 3175     ch = '\0';
 3176     while (!fragment_consumed(input)) {
 3177     char prev_ch = ch;
 3178     ch = peek_fragment(input);
 3179     /* ';' (resync) and '@' (macrograph) are not recognized in strings */
 3180     if (prev_ch == open_quote_ch) {
 3181         if (ch == open_quote_ch) {
 3182         if (outlen < maxlen) {
 3183             out[outlen] = ch;
 3184         }
 3185         outlen++;
 3186         pop_fragment(input);
 3187         ch = '\0';
 3188         continue;
 3189         }
 3190         if (outlen < maxlen)
 3191         out[outlen] = '\0';
 3192         else
 3193         out[maxlen] = '\0';
 3194         return 1;
 3195     }
 3196     if (ch == '\0')
 3197         break;
 3198     if (ch != open_quote_ch) {
 3199         if (outlen < maxlen)
 3200         out[outlen] = ch;
 3201         outlen++;
 3202     }
 3203     pop_fragment(input);
 3204     }
 3205     if (ch == open_quote_ch) {
 3206     pop_fragment(input);
 3207     if (outlen < maxlen)
 3208         out[outlen] = '\0';
 3209     else
 3210         out[maxlen] = '\0';
 3211     return 1;
 3212     }
 3213     /* FIXME: handle multiple strings concatenated with commas */
 3214 
 3215     TRACE(("DATA_ERROR: end of input before closing quote\n"));
 3216     return 0;
 3217 }
 3218 
 3219 static int
 3220 extract_regis_parenthesized_data(RegisDataFragment *input,
 3221                  RegisDataFragment *output)
 3222 {
 3223     char ch;
 3224     char open_quote_ch;
 3225     int nesting;
 3226 
 3227     assert(input);
 3228     assert(output);
 3229 
 3230     output->start = &input->start[input->pos];
 3231     output->len = 0U;
 3232     output->pos = 0U;
 3233 
 3234     if (input->pos >= input->len)
 3235     return 0;
 3236 
 3237     ch = input->start[input->pos];
 3238     if (ch != '(')
 3239     return 0;
 3240     input->pos++;
 3241     output->start++;
 3242     nesting = 1;
 3243     open_quote_ch = '\0';
 3244 
 3245     ch = '\0';
 3246     for (; input->pos < input->len; input->pos++, output->len++) {
 3247     char prev_ch = ch;
 3248     ch = input->start[input->pos];
 3249     if (ch == '\'' || ch == '"') {
 3250         if (open_quote_ch == '\0') {
 3251         open_quote_ch = ch;
 3252         } else {
 3253         if (ch == prev_ch && prev_ch == open_quote_ch) {
 3254             ch = '\0';
 3255         } else if (ch == open_quote_ch) {
 3256             open_quote_ch = '\0';
 3257         }
 3258         }
 3259         continue;
 3260     }
 3261     if (open_quote_ch != '\0')
 3262         continue;
 3263 
 3264     if (ch == ';') {
 3265         TRACE(("leaving parenthesized data nested %d levels deep due to command termination character\n",
 3266            nesting));
 3267         break;
 3268     }
 3269     if (ch == '(')
 3270         nesting++;
 3271     if (ch == ')') {
 3272         nesting--;
 3273         if (nesting == 0) {
 3274         input->pos++;
 3275         return 1;
 3276         }
 3277     }
 3278     }
 3279 
 3280     TRACE(("DATA_ERROR: end of input before closing paren (%d levels deep)\n",
 3281        nesting));
 3282     return 0;
 3283 }
 3284 
 3285 static int
 3286 extract_regis_option(RegisDataFragment *input,
 3287              char *option,
 3288              RegisDataFragment *output)
 3289 {
 3290     char ch;
 3291     int paren_level, bracket_level;
 3292     char open_quote_ch;
 3293 
 3294     assert(input);
 3295     assert(option);
 3296     assert(output);
 3297 
 3298     /* LETTER suboptions* value? */
 3299     /*
 3300      * FIXME: what are the rules for using separate parens vs. sharing between
 3301      * options?
 3302      */
 3303 
 3304     output->start = &input->start[input->pos];
 3305     output->len = 0U;
 3306     output->pos = 0U;
 3307 
 3308     if (input->pos >= input->len) {
 3309     return 0;
 3310     }
 3311 
 3312     ch = input->start[input->pos];
 3313     /* FIXME: are options always letters or are some special characters ok? */
 3314     if (ch == ';' || ch == ',' ||
 3315     ch == '(' || ch == ')' ||
 3316     ch == '[' || ch == ']' ||
 3317     ch == '"' || ch == '\'' ||
 3318     isdigit(CharOf(ch))) {
 3319     return 0;
 3320     }
 3321     *option = ch;
 3322     input->pos++;
 3323     output->start++;
 3324     paren_level = 0;
 3325     bracket_level = 0;
 3326 
 3327     open_quote_ch = '\0';
 3328     for (; input->pos < input->len; input->pos++, output->len++) {
 3329     ch = input->start[input->pos];
 3330     TRACE(("looking at char '%c' in option '%c'\n", ch, *option));
 3331     /* FIXME: any special rules for commas? */
 3332     /* FIXME: handle escaped quotes */
 3333     if (ch == '\'' || ch == '"') {
 3334         if (open_quote_ch == ch) {
 3335         open_quote_ch = '\0';
 3336         } else {
 3337         open_quote_ch = ch;
 3338         }
 3339         continue;
 3340     }
 3341     if (open_quote_ch != '\0')
 3342         continue;
 3343     if (ch == '(') {
 3344         paren_level++;
 3345     }
 3346     if (ch == ')') {
 3347         paren_level--;
 3348         if (paren_level < 0) {
 3349         TRACE(("DATA_ERROR: found ReGIS option has value with too many close parens \"%c\"\n",
 3350                *option));
 3351         return 0;
 3352         }
 3353     }
 3354     if (ch == '[') {
 3355         bracket_level++;
 3356     }
 3357     if (ch == ']') {
 3358         bracket_level--;
 3359         if (bracket_level < 0) {
 3360         TRACE(("DATA_ERROR: found ReGIS option has value with too many close brackets \"%c\"\n",
 3361                *option));
 3362         return 0;
 3363         }
 3364     }
 3365     if (paren_level == 0 && bracket_level == 0) {
 3366         /*
 3367          * Top-level commas indicate the end of this option and the start of
 3368          * another.
 3369          */
 3370         if (ch == ',')
 3371         break;
 3372         /*
 3373          * Top-level command/option/suboption names also indicate the end of
 3374          * this option.  "E" is valid as the exponent indicator in a numeric
 3375          * parameter.
 3376          */
 3377         if (ch != 'E' && ch != 'e' &&
 3378         ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z')))
 3379         break;
 3380     }
 3381     if (ch == ';')
 3382         break;
 3383     }
 3384     if (paren_level != 0) {
 3385     TRACE(("DATA_ERROR: mismatched parens in argument to ReGIS option \"%c\"\n",
 3386            *option));
 3387     return 0;
 3388     }
 3389     if (bracket_level != 0) {
 3390     TRACE(("DATA_ERROR: mismatched brackets in argument to ReGIS option \"%c\"\n",
 3391            *option));
 3392     return 0;
 3393     }
 3394 
 3395     TRACE(("found ReGIS option and value \"%c\" \"%s\"\n",
 3396        *option,
 3397        fragment_to_tempstr(output)));
 3398     return 1;
 3399 }
 3400 
 3401 static int
 3402 regis_num_to_int(RegisDataFragment const *input, int *out)
 3403 {
 3404     char ch;
 3405 
 3406     assert(input);
 3407     assert(out);
 3408 
 3409     /* FIXME: handle exponential notation and rounding */
 3410     /* FIXME: check for junk after the number */
 3411     ch = peek_fragment(input);
 3412     if (!isdigit(CharOf(ch)) &&
 3413     ch != '+' &&
 3414     ch != '-') {
 3415     *out = 0;
 3416     return 0;
 3417     }
 3418 
 3419     TRACE(("converting \"%s\" to an int\n", fragment_to_tempstr(input)));
 3420     *out = atoi(fragment_to_tempstr(input));
 3421     return 1;
 3422 }
 3423 
 3424 static int
 3425 load_regis_colorspec(RegisGraphicsContext const *context,
 3426              RegisDataFragment const *input,
 3427              short *r_out, short *g_out, short *b_out)
 3428 {
 3429     RegisDataFragment colorspec;
 3430     short r = -1, g = -1, b = -1;
 3431     short l = -1;
 3432     int simple;
 3433 
 3434     assert(context);
 3435     assert(input);
 3436     assert(r_out);
 3437     assert(g_out);
 3438     assert(b_out);
 3439 
 3440     copy_fragment(&colorspec, input);
 3441     TRACE(("colorspec option: \"%s\"\n", fragment_to_tempstr(&colorspec)));
 3442 
 3443     skip_regis_whitespace(&colorspec);
 3444     simple = 0;
 3445     if (fragment_remaining(&colorspec) == 1U) {
 3446     simple = 1;
 3447     } else if (fragment_remaining(&colorspec) > 1U) {
 3448     char after = get_fragment(&colorspec, 1U);
 3449     if (IsSpace(after))
 3450         simple = 1;
 3451     }
 3452     if (simple) {
 3453     char ch = pop_fragment(&colorspec);
 3454 
 3455     TRACE(("got ReGIS RGB colorspec pattern '%c' with arguments: \"%s\"\n",
 3456            ch, fragment_to_tempstr(&colorspec)));
 3457     switch (ch) {
 3458     case 'D':
 3459     case 'd':
 3460         r = 0;
 3461         g = 0;
 3462         b = 0;
 3463         l = 0;
 3464         break;
 3465     case 'R':
 3466     case 'r':
 3467         r = 100;
 3468         g = 0;
 3469         b = 0;
 3470         l = 46;
 3471         break;
 3472     case 'G':
 3473     case 'g':
 3474         r = 0;
 3475         g = 100;
 3476         b = 0;
 3477         l = 50;
 3478         break;
 3479     case 'B':
 3480     case 'b':
 3481         r = 0;
 3482         g = 0;
 3483         b = 100;
 3484         l = 50;
 3485         break;
 3486     case 'C':
 3487     case 'c':
 3488         r = 0;
 3489         g = 100;
 3490         b = 100;
 3491         l = 50;
 3492         break;
 3493     case 'Y':
 3494     case 'y':
 3495         r = 100;
 3496         g = 100;
 3497         b = 0;
 3498         l = 50;
 3499         break;
 3500     case 'M':
 3501     case 'm':
 3502         r = 100;
 3503         g = 0;
 3504         b = 100;
 3505         l = 50;
 3506         break;
 3507     case 'W':
 3508     case 'w':
 3509         r = 100;
 3510         g = 100;
 3511         b = 100;
 3512         l = 100;
 3513         break;
 3514     default:
 3515         TRACE(("DATA_ERROR: unknown RGB color name: \"%c\"\n", ch));
 3516         return 0;
 3517     }
 3518     } else {
 3519     RegisDataFragment num;
 3520     int max, val;
 3521     char comp;
 3522     short h = -1;
 3523     short s = -1;
 3524 
 3525     while (!fragment_consumed(&colorspec)) {
 3526         if (skip_regis_whitespace(&colorspec))
 3527         continue;
 3528 
 3529         comp = pop_fragment(&colorspec);
 3530         switch (comp) {
 3531         case ',':
 3532         /* not sure if this is valid, but it is easy to handle */
 3533         continue;
 3534         case 'H':
 3535         case 'h':
 3536         max = 360;
 3537         comp = 'H';
 3538         break;
 3539         case 'L':
 3540         case 'l':
 3541         max = 100;
 3542         comp = 'L';
 3543         break;
 3544         case 'S':
 3545         case 's':
 3546         max = 100;
 3547         comp = 'S';
 3548         break;
 3549 #ifdef ENABLE_RGB_COLORSPECS
 3550         case 'R':       /* RLogin extension */
 3551         case 'r':
 3552         max = 100;
 3553         comp = 'R';
 3554         break;
 3555         case 'G':       /* RLogin extension */
 3556         case 'g':
 3557         max = 100;
 3558         comp = 'G';
 3559         break;
 3560         case 'B':       /* RLogin extension */
 3561         case 'b':
 3562         max = 100;
 3563         comp = 'B';
 3564         break;
 3565 #endif
 3566         default:
 3567         TRACE(("DATA_ERROR: unrecognized component in colorspec: '%c'\n",
 3568                comp));
 3569         return 0;
 3570         }
 3571 
 3572         skip_regis_whitespace(&colorspec);
 3573         if (!extract_regis_num(&colorspec, &num)) {
 3574         TRACE(("DATA_ERROR: expected int after '%c' component in colorspec: \"%s\"\n",
 3575                comp, fragment_to_tempstr(&colorspec)));
 3576         return 0;
 3577         }
 3578         if (!regis_num_to_int(&num, &val)) {
 3579         TRACE(("DATA_ERROR: component value %s is not a number\n",
 3580                fragment_to_tempstr(&num)));
 3581         return 0;
 3582         }
 3583         /* FIXME: error, truncate, wrap, ...? */
 3584         if (val < 0 || val > max) {
 3585         TRACE(("DATA_ERROR: component value %d out of range\n", val));
 3586         return 0;
 3587         }
 3588 
 3589         switch (comp) {
 3590         case 'H':
 3591         h = (short) val;
 3592         break;
 3593         case 'L':
 3594         l = (short) val;
 3595         break;
 3596         case 'S':
 3597         s = (short) val;
 3598         break;
 3599         case 'R':
 3600         r = (short) val;
 3601         break;
 3602         case 'G':
 3603         g = (short) val;
 3604         break;
 3605         case 'B':
 3606         b = (short) val;
 3607         break;
 3608         }
 3609     }
 3610 
 3611     if (h >= 0 && l >= 0 && s >= 0 && r < 0 && g < 0 && b < 0) {
 3612         TRACE(("found HLS colorspec to be converted: %hd,%hd,%hd\n",
 3613            h, l, s));
 3614         hls2rgb(h, l, s, &r, &g, &b);
 3615         TRACE(("converted to RGB: %hd,%hd,%hd\n", r, g, b));
 3616     } else if (h < 0 && l < 0 && s < 0 && r >= 0 && g >= 0 && b >= 0) {
 3617         TRACE(("found RGB colorspec: %hd,%hd,%hd\n", r, g, b));
 3618         l = (short) ((MIN3(r, g, b) + MAX3(r, g, b)) / 2);
 3619         TRACE(("calculated L: %d\n", l));
 3620     } else if (h < 0 && l >= 0 && s < 0 && r < 0 && g < 0 && b < 0) {
 3621         TRACE(("found L colorspec to be converted: %hd,%hd,%hd\n",
 3622            h, l, s));
 3623         hls2rgb(0, l, 0, &r, &g, &b);
 3624         TRACE(("converted to RGB: %hd,%hd,%hd\n", r, g, b));
 3625     } else {
 3626         TRACE(("DATA_ERROR: unrecognized colorspec format\n"));
 3627         return 0;
 3628     }
 3629     }
 3630 
 3631     /*
 3632      * The VT240 and VT330 models convert to the closest grayscale value.
 3633      */
 3634     if (context->graphics_termid == 240 || context->graphics_termid == 330) {
 3635     hls2rgb(0, l, 0, &r, &g, &b);
 3636     TRACE(("converted to grayscale: %hd,%hd,%hd\n", r, g, b));
 3637     }
 3638 
 3639     *r_out = r;
 3640     *g_out = g;
 3641     *b_out = b;
 3642 
 3643     skip_regis_whitespace(&colorspec);
 3644     if (!fragment_consumed(&colorspec)) {
 3645     char skip;
 3646 
 3647     skip = pop_fragment(&colorspec);
 3648     (void) skip;        /* variable needed only if tracing */
 3649     TRACE(("DATA_ERROR: ignoring unexpected character in ReGIS colorspec \"%c\"\n",
 3650            skip));
 3651     }
 3652 
 3653     return 1;
 3654 }
 3655 
 3656 static int
 3657 load_regis_regnum_or_colorspec(RegisGraphicsContext const *context,
 3658                    RegisDataFragment const *input,
 3659                    RegisterNum *out)
 3660 {
 3661     int val;
 3662     RegisDataFragment colorspec;
 3663     RegisDataFragment num;
 3664     RegisDataFragment coloroption;
 3665 
 3666     copy_fragment(&colorspec, input);
 3667     TRACE(("looking at colorspec pattern: \"%s\"\n",
 3668        fragment_to_tempstr(&colorspec)));
 3669 
 3670     skip_regis_whitespace(&colorspec);
 3671 
 3672     if (extract_regis_num(&colorspec, &num)) {
 3673     if (!regis_num_to_int(&num, &val)) {
 3674         TRACE(("DATA_ERROR: colorspec value %s is not a valid register\n",
 3675            fragment_to_tempstr(&num)));
 3676         return 0;
 3677     }
 3678     if (val < 0) {
 3679         /* FIXME: error, truncate, wrap, ...? */
 3680         TRACE(("DATA_ERROR: ignoring negative colorspec value: %d\n", val));
 3681         return 0;
 3682     }
 3683     if (val >= (int) context->destination_graphic->valid_registers) {
 3684         /* FIXME: error, truncate, wrap, ...? */
 3685         TRACE(("DATA_ERROR: colorspec value %d is too big; wrapping\n",
 3686            val));
 3687         val %= (int) context->destination_graphic->valid_registers;
 3688     }
 3689 
 3690     TRACE(("colorspec contains index for register %u\n", val));
 3691     *out = (RegisterNum) val;
 3692 
 3693     skip_regis_whitespace(&colorspec);
 3694     if (!fragment_consumed(&colorspec)) {
 3695         char skip;
 3696 
 3697         skip = pop_fragment(&colorspec);
 3698         (void) skip;    /* variable needed only if tracing */
 3699         TRACE(("DATA_ERROR: unexpected character after register \"%c\"\n",
 3700            skip));
 3701         return 0;
 3702     }
 3703 
 3704     return 1;
 3705     }
 3706 
 3707     if (extract_regis_parenthesized_data(&colorspec, &coloroption)) {
 3708     short r, g, b;
 3709 
 3710     if (!load_regis_colorspec(context, &coloroption, &r, &g, &b)) {
 3711         TRACE(("unable to parse colorspec\n"));
 3712         return 0;
 3713     }
 3714 
 3715     *out = find_color_register(context->destination_graphic->color_registers,
 3716                    r, g, b);
 3717     TRACE(("colorspec maps to closest register %u\n", *out));
 3718 
 3719     return 1;
 3720     }
 3721 
 3722     TRACE(("expected register number or colorspec, but found: \"%s\"\n",
 3723        fragment_to_tempstr(&colorspec)));
 3724     return 0;
 3725 }
 3726 
 3727 static int
 3728 to_scaled_int(char const *num, int scale, int *value)
 3729 {
 3730     unsigned long whole, frac;
 3731     char *end;
 3732 
 3733     /* FIXME: handle whitespace? how about trailing junk? */
 3734     whole = strtoul(num, &end, 10);
 3735     if (end[0] == '.') {
 3736     char temp[5] = "0000";
 3737 
 3738     if (end[1] != '\0') {
 3739         temp[0] = end[1];
 3740         if (end[2] != '\0') {
 3741         temp[1] = end[2];
 3742         if (end[3] != '\0') {
 3743             temp[2] = end[3];
 3744             if (end[4] != '\0') {
 3745             temp[3] = end[4];
 3746             }
 3747         }
 3748         }
 3749     }
 3750     frac = strtoul(temp, NULL, 10);
 3751     } else if (end[0] == '\0' || end[0] == ',' || IsSpace(end[0])) {
 3752     frac = 0;
 3753     } else {
 3754     TRACE(("unexpected character %c in number %s\n", end[0], num));
 3755     return 0;
 3756     }
 3757 
 3758     *value = (int) (whole * (unsigned) scale +
 3759             (frac * (unsigned) scale) / 10000);
 3760 
 3761     return 1;
 3762 }
 3763 
 3764 static int
 3765 load_regis_raw_extent(char const *extent, int *relx, int *rely,
 3766               int *xloc, int *yloc, int scale)
 3767 {
 3768     int xsign, ysign;
 3769     char const *xpart;
 3770     char const *ypart;
 3771 
 3772     xpart = extent;
 3773     if ((ypart = strchr(extent, ','))) {
 3774     ypart++;
 3775     } else {
 3776     ypart = "";
 3777     }
 3778 
 3779     while (IsSpace(xpart[0]))
 3780     xpart++;
 3781     while (IsSpace(ypart[0]))
 3782     ypart++;
 3783 
 3784     if (xpart[0] == '-') {
 3785     xsign = -1;
 3786     xpart++;
 3787     } else if (xpart[0] == '+') {
 3788     xsign = +1;
 3789     xpart++;
 3790     } else {
 3791     xsign = 0;
 3792     }
 3793     if (ypart[0] == '-') {
 3794     ysign = -1;
 3795     ypart++;
 3796     } else if (ypart[0] == '+') {
 3797     ysign = +1;
 3798     ypart++;
 3799     } else {
 3800     ysign = 0;
 3801     }
 3802 
 3803     if (xpart[0] == '\0' || xpart[0] == ',') {
 3804     *relx = 1;
 3805     *xloc = 0;
 3806     } else if (xsign == 0) {
 3807     int val;
 3808 
 3809     if (!to_scaled_int(xpart, scale, &val))
 3810         return 0;
 3811     *relx = 0;
 3812     *xloc = val;
 3813     } else {
 3814     int val;
 3815 
 3816     if (!to_scaled_int(xpart, scale, &val))
 3817         return 0;
 3818     *relx = 1;
 3819     *xloc = xsign * val;
 3820     }
 3821     if (ypart[0] == '\0') {
 3822     *rely = 1;
 3823     *yloc = 0;
 3824     } else if (ysign == 0) {
 3825     int val;
 3826 
 3827     if (!to_scaled_int(ypart, scale, &val))
 3828         return 0;
 3829     *rely = 0;
 3830     *yloc = val;
 3831     } else {
 3832     int val;
 3833 
 3834     if (!to_scaled_int(ypart, scale, &val))
 3835         return 0;
 3836     *rely = 1;
 3837     *yloc = ysign * val;
 3838     }
 3839 
 3840     return 1;
 3841 }
 3842 
 3843 static int
 3844 load_regis_mult_extent(char const *extent, int *w, int *h)
 3845 {
 3846     int relx, rely;
 3847     int px, py;
 3848 
 3849     if (!load_regis_raw_extent(extent, &relx, &rely, &px, &py, 1)) {
 3850     TRACE(("invalid coordinates in extent %s\n", extent));
 3851     return 0;
 3852     }
 3853     if (relx | rely) {
 3854     TRACE(("invalid relative value in multiplier extent %s\n", extent));
 3855     return 0;
 3856     }
 3857 
 3858     *w = px;
 3859     *h = py;
 3860 
 3861     return 1;
 3862 }
 3863 
 3864 static int
 3865 load_regis_pixel_extent(char const *extent, int origx, int origy,
 3866             int *xloc, int *yloc)
 3867 {
 3868     int relx, rely;
 3869     int px, py;
 3870 
 3871     if (!load_regis_raw_extent(extent, &relx, &rely, &px, &py, 1)) {
 3872     TRACE(("invalid coordinates in extent %s\n", extent));
 3873     return 0;
 3874     }
 3875 
 3876     *xloc = px;
 3877     *yloc = py;
 3878 
 3879     if (relx)
 3880     *xloc += origx;
 3881     if (rely)
 3882     *yloc += origy;
 3883 
 3884     return 1;
 3885 }
 3886 
 3887 #define COORD_SCALE 1000
 3888 
 3889 static int
 3890 load_regis_coord_extent(RegisGraphicsContext const *context, char const *extent,
 3891             int origx, int origy, int *xloc, int *yloc)
 3892 {
 3893     int relx, rely;
 3894     int ux, uy;
 3895 
 3896     if (!load_regis_raw_extent(extent, &relx, &rely, &ux, &uy, COORD_SCALE)) {
 3897     TRACE(("invalid coordinates in extent %s\n", extent));
 3898     return 0;
 3899     }
 3900 
 3901     if (relx) {
 3902     const int px = SCALE_XCOORD(context, ux, COORD_SCALE);
 3903     TRACE(("converted relative user X coord %.03f to relative pixel X coord %d (width=%d xoff=%d xdiv=%d)\n",
 3904            ux / (double) COORD_SCALE, px, context->width,
 3905            context->x_off, context->x_div));
 3906     *xloc = origx + px;
 3907     } else {
 3908     const int px = TRANSLATE_XCOORD(context, ux, COORD_SCALE);
 3909     TRACE(("converted absolute user X coord %.03f to absolute pixel X coord %d\n",
 3910            ux / (double) COORD_SCALE, px));
 3911     *xloc = px;
 3912     }
 3913     if (rely) {
 3914     const int py = SCALE_YCOORD(context, uy, COORD_SCALE);
 3915     TRACE(("converted relative user Y coord %.03f to relative pixel Y coord %d (height=%d yoff=%d ydiv=%d)\n",
 3916            uy / (double) COORD_SCALE, py, context->height,
 3917            context->y_off, context->y_div));
 3918     *yloc = origy + py;
 3919     } else {
 3920     const int py = TRANSLATE_YCOORD(context, uy, COORD_SCALE);
 3921     TRACE(("converted absolute user Y coord %.03f to absolute pixel Y coord %d\n",
 3922            uy / (double) COORD_SCALE, py));
 3923     *yloc = py;
 3924     }
 3925 
 3926     return 1;
 3927 }
 3928 
 3929 static int
 3930 load_regis_raw_pixelvector_digit(char const *pixelvector,
 3931                  unsigned *offset,
 3932                  int *dx, int *dy, int mul)
 3933 {
 3934     switch (pixelvector[*offset]) {
 3935     case '0':
 3936     *dx += mul;
 3937     break;
 3938     case '1':
 3939     *dx += mul;
 3940     *dy -= mul;
 3941     break;
 3942     case '2':
 3943     *dy -= mul;
 3944     break;
 3945     case '3':
 3946     *dx -= mul;
 3947     *dy -= mul;
 3948     break;
 3949     case '4':
 3950     *dx -= mul;
 3951     break;
 3952     case '5':
 3953     *dx -= mul;
 3954     *dy += mul;
 3955     break;
 3956     case '6':
 3957     *dy += mul;
 3958     break;
 3959     case '7':
 3960     *dx += mul;
 3961     *dy += mul;
 3962     break;
 3963     default:
 3964     return 0;
 3965     }
 3966 
 3967     (*offset)++;
 3968     return 1;
 3969 }
 3970 
 3971 static int
 3972 load_regis_pixel_pixelvector(char const *pixelvector,
 3973                  int mul,
 3974                  int origx, int origy,
 3975                  int *xloc, int *yloc)
 3976 {
 3977     int found = 0;
 3978     int px = 0, py = 0;
 3979     unsigned offset = 0U;
 3980     while (load_regis_raw_pixelvector_digit(pixelvector, &offset,
 3981                         &px, &py,
 3982                         mul))
 3983     found = 1;
 3984     if (pixelvector[offset] != '\0') {
 3985     TRACE(("DATA_ERROR: ignoring unknown pixel vector digits: \"%s\"\n",
 3986            &pixelvector[offset]));
 3987     }
 3988 
 3989     *xloc = origx + px;
 3990     *yloc = origy + py;
 3991 
 3992     return found;
 3993 }
 3994 
 3995 static int
 3996 load_regis_coord_pixelvector(RegisGraphicsContext const *context,
 3997                  char const *pixelvector,
 3998                  int origx, int origy,
 3999                  int *xloc, int *yloc)
 4000 {
 4001     const int mul = (int) (context->temporary_write_controls.pv_multiplier
 4002                * COORD_SCALE);
 4003     int found = 0;
 4004     int ux = 0, uy = 0;
 4005     unsigned offset = 0U;
 4006 
 4007     while (load_regis_raw_pixelvector_digit(pixelvector, &offset,
 4008                         &ux, &uy,
 4009                         mul))
 4010     found = 1;
 4011     if (pixelvector[offset] != '\0') {
 4012     TRACE(("DATA_ERROR: ignoring unknown pixel vector digits: \"%s\"\n",
 4013            &pixelvector[offset]));
 4014     } {
 4015     const int px = SCALE_XCOORD(context, ux, COORD_SCALE);
 4016     const int py = SCALE_YCOORD(context, uy, COORD_SCALE);
 4017 
 4018     TRACE(("converted relative X coord %.03f to relative pixel X coord %d (width=%d xoff=%d xdiv=%d)\n",
 4019            ux / (double) COORD_SCALE, px, context->width,
 4020            context->x_off, context->x_div));
 4021     *xloc = origx + px;
 4022 
 4023     TRACE(("converted relative Y coord %.03f to relative pixel Y coord %d (height=%d yoff=%d ydiv=%d)\n",
 4024            uy / (double) COORD_SCALE, py, context->height,
 4025            context->y_off, context->y_div));
 4026     *yloc = origy + py;
 4027     }
 4028 
 4029     return found;
 4030 }
 4031 
 4032 static int
 4033 load_regis_coord_pixelvector_step(RegisGraphicsContext const *context,
 4034                   char const *pixelvector,
 4035                   unsigned *offset,
 4036                   int origx, int origy,
 4037                   int *xloc, int *yloc)
 4038 {
 4039     const int mul = (int) (context->temporary_write_controls.pv_multiplier
 4040                * COORD_SCALE);
 4041     int found = 0;
 4042     int ux = 0, uy = 0;
 4043     if (load_regis_raw_pixelvector_digit(pixelvector, offset, &ux, &uy, mul))
 4044     found = 1;
 4045     if (!found && pixelvector[*offset] != '\0') {
 4046     TRACE(("DATA_ERROR: ignoring unknown pixel vector digits: \"%s\"\n",
 4047            &pixelvector[*offset]));
 4048     } {
 4049     const int px = SCALE_XCOORD(context, ux, COORD_SCALE);
 4050     const int py = SCALE_YCOORD(context, uy, COORD_SCALE);
 4051 
 4052     TRACE(("converted relative X coord %.03f to relative pixel X coord %d (width=%d xoff=%d xdiv=%d)\n",
 4053            ux / (double) COORD_SCALE, px, context->width,
 4054            context->x_off, context->x_div));
 4055     *xloc = origx + px;
 4056 
 4057     TRACE(("converted relative Y coord %.03f to relative pixel Y coord %d (height=%d yoff=%d ydiv=%d)\n",
 4058            uy / (double) COORD_SCALE, py, context->height,
 4059            context->y_off, context->y_div));
 4060     *yloc = origy + py;
 4061     }
 4062 
 4063     return found;
 4064 }
 4065 
 4066 static int
 4067 load_regis_write_control(RegisParseState *state,
 4068              RegisGraphicsContext const *context,
 4069              int cur_x, int cur_y,
 4070              int option,
 4071              RegisDataFragment *arg,
 4072              RegisWriteControls *out)
 4073 {
 4074     TRACE(("checking write control option \"%c\" with arg \"%s\"\n",
 4075        option, fragment_to_tempstr(arg)));
 4076     switch (option) {
 4077     case 'A':
 4078     case 'a':
 4079     TRACE(("write control alternate display method \"%s\"\n",
 4080            fragment_to_tempstr(arg)));
 4081     {
 4082         int val;
 4083         if (!regis_num_to_int(arg, &val) || val < 0 || val >= 1) {
 4084         TRACE(("DATA_ERROR: interpreting out of range value as 0 FIXME\n"));
 4085         break;
 4086         }
 4087         if (val == 1) {
 4088         TRACE(("ERROR: blink display method not supported FIXME\n"));
 4089         }
 4090     }
 4091     break;
 4092     case 'C':
 4093     case 'c':
 4094     TRACE(("write control compliment writing mode \"%s\"\n",
 4095            fragment_to_tempstr(arg)));
 4096     out->write_style = WRITE_STYLE_COMPLEMENT;
 4097     break;
 4098     case 'E':
 4099     case 'e':
 4100     TRACE(("write control erase writing mode \"%s\"\n",
 4101            fragment_to_tempstr(arg)));
 4102     out->write_style = WRITE_STYLE_ERASE;
 4103     break;
 4104     case 'F':
 4105     case 'f':
 4106     TRACE(("write control plane write mask \"%s\"\n",
 4107            fragment_to_tempstr(arg)));
 4108     {
 4109         int val;
 4110         if (!regis_num_to_int(arg, &val) ||
 4111         val < 0 || val >= (int) context->destination_graphic->valid_registers) {
 4112         TRACE(("DATA_ERROR: interpreting out of range value as 0 FIXME\n"));
 4113         out->plane_mask = 0U;
 4114         } else {
 4115         out->plane_mask = (unsigned) val;
 4116         }
 4117     }
 4118     break;
 4119     case 'I':
 4120     case 'i':
 4121     TRACE(("write control foreground color \"%s\"\n",
 4122            fragment_to_tempstr(arg)));
 4123     if (!load_regis_regnum_or_colorspec(context, arg, &out->foreground)) {
 4124         TRACE(("DATA_ERROR: write control foreground color specifier not recognized: \"%s\"\n",
 4125            fragment_to_tempstr(arg)));
 4126         return 0;
 4127     }
 4128     break;
 4129     case 'L':
 4130     case 'l':
 4131     TRACE(("write control line width \"%s\" (FIXME: currently ignored)\n",
 4132            fragment_to_tempstr(arg)));
 4133     {
 4134         int val;
 4135         if (!regis_num_to_int(arg, &val) ||
 4136         val < 0 || val >= (int) 9) {
 4137         TRACE(("interpreting out of range value as 1 FIXME\n"));
 4138         out->line_width = 1U;
 4139         } else {
 4140         out->line_width = (unsigned) val;
 4141         }
 4142     }
 4143     break;
 4144     case 'M':
 4145     case 'm':
 4146     TRACE(("write control found pixel multiplication factor \"%s\"\n",
 4147            fragment_to_tempstr(arg)));
 4148     {
 4149         int val;
 4150         if (!regis_num_to_int(arg, &val) || val <= 0) {
 4151         TRACE(("interpreting out of range value %d as 1 FIXME\n", val));
 4152         out->pv_multiplier = 1U;
 4153         } else {
 4154         out->pv_multiplier = (unsigned) val;
 4155         }
 4156     }
 4157     break;
 4158     case 'N':
 4159     case 'n':
 4160     TRACE(("write control negative pattern control \"%s\"\n",
 4161            fragment_to_tempstr(arg)));
 4162     {
 4163         int val;
 4164         if (!regis_num_to_int(arg, &val)) {
 4165         val = -1;
 4166         }
 4167         switch (val) {
 4168         default:
 4169         TRACE(("interpreting out of range value %d as 0 FIXME\n", val));
 4170         out->invert_pattern = 0U;
 4171         break;
 4172         case 0:
 4173         out->invert_pattern = 0U;
 4174         break;
 4175         case 1:
 4176         out->invert_pattern = 1U;
 4177         break;
 4178         }
 4179     }
 4180     break;
 4181     case 'P':
 4182     case 'p':
 4183     TRACE(("write control found pattern control \"%s\"\n",
 4184            fragment_to_tempstr(arg)));
 4185     {
 4186         RegisDataFragment suboptionset;
 4187         RegisDataFragment suboptionarg;
 4188         RegisDataFragment item;
 4189         char suboption;
 4190 
 4191         while (!fragment_consumed(arg)) {
 4192         if (skip_regis_whitespace(arg))
 4193             continue;
 4194 
 4195         TRACE(("looking for option in \"%s\"\n",
 4196                fragment_to_tempstr(arg)));
 4197         if (extract_regis_parenthesized_data(arg, &suboptionset)) {
 4198             TRACE(("got write pattern suboptionset: \"%s\"\n",
 4199                fragment_to_tempstr(&suboptionset)));
 4200             while (!fragment_consumed(&suboptionset)) {
 4201             skip_regis_whitespace(&suboptionset);
 4202             if (extract_regis_option(&suboptionset, &suboption,
 4203                          &suboptionarg)) {
 4204                 skip_regis_whitespace(&suboptionarg);
 4205                 TRACE(("inspecting write pattern suboption \"%c\" with value \"%s\"\n",
 4206                    suboption,
 4207                    fragment_to_tempstr(&suboptionarg)));
 4208                 switch (suboption) {
 4209                 case 'M':
 4210                 case 'm':
 4211                 TRACE(("found pattern multiplier \"%s\"\n",
 4212                        fragment_to_tempstr(&suboptionarg)));
 4213                 {
 4214                     RegisDataFragment num;
 4215                     int val;
 4216 
 4217                     if (extract_regis_num(&suboptionarg,
 4218                               &num)) {
 4219                     if (!regis_num_to_int(&num, &val)
 4220                         || val < 1) {
 4221                         TRACE(("interpreting out of range pattern multiplier \"%s\" as 2 FIXME\n",
 4222                            fragment_to_tempstr(&num)));
 4223                         out->pattern_multiplier = 2U;
 4224                     } else {
 4225                         out->pattern_multiplier =
 4226                         (unsigned) val;
 4227                     }
 4228                     skip_regis_whitespace(&suboptionarg);
 4229                     }
 4230 
 4231                     if (!fragment_consumed(&suboptionarg)) {
 4232                     TRACE(("DATA_ERROR: unknown content after pattern multiplier \"%s\"\n",
 4233                            fragment_to_tempstr(&suboptionarg)));
 4234                     return 0;
 4235                     }
 4236                 }
 4237                 break;
 4238                 default:
 4239                 TRACE(("DATA_ERROR: unknown ReGIS write pattern suboption '%c' arg \"%s\"\n",
 4240                        suboption,
 4241                        fragment_to_tempstr(&suboptionarg)));
 4242                 return 0;
 4243                 }
 4244                 continue;
 4245             }
 4246 
 4247             TRACE(("DATA_ERROR: skipping unknown token in pattern control suboptionset (expecting option): \"%s\"\n",
 4248                    fragment_to_tempstr(&suboptionset)));
 4249             pop_fragment(&suboptionset);
 4250             }
 4251             continue;
 4252         }
 4253 
 4254         TRACE(("looking for int in \"%s\"\n",
 4255                fragment_to_tempstr(arg)));
 4256         if (extract_regis_num(arg, &item)) {
 4257             if (peek_fragment(&item) == '0' ||
 4258             peek_fragment(&item) == '1') {
 4259             unsigned pattern = 0U;
 4260             unsigned bitcount;
 4261 
 4262             TRACE(("converting pattern bits \"%s\"\n",
 4263                    fragment_to_tempstr(&item)));
 4264             for (bitcount = 0;; bitcount++) {
 4265                 char ch = pop_fragment(&item);
 4266                 if (ch == '\0')
 4267                 break;
 4268                 switch (ch) {
 4269                 case '0':
 4270                 if (bitcount < MAX_PATTERN_BITS) {
 4271                     pattern <<= 1U;
 4272                 }
 4273                 break;
 4274                 case '1':
 4275                 if (bitcount < MAX_PATTERN_BITS) {
 4276                     pattern <<= 1U;
 4277                     pattern |= 1U;
 4278                 }
 4279                 break;
 4280                 default:
 4281                 TRACE(("DATA_ERROR: unknown ReGIS write pattern bit value \"%c\"\n",
 4282                        ch));
 4283                 return 0;
 4284                 }
 4285             }
 4286 
 4287             if (bitcount > 0U) {
 4288                 unsigned extrabits;
 4289 
 4290                 for (extrabits = 0;
 4291                  bitcount + extrabits < MAX_PATTERN_BITS;
 4292                  extrabits++) {
 4293                 if (pattern & (1U << (bitcount - 1U))) {
 4294                     pattern <<= 1U;
 4295                     pattern |= 1U;
 4296                 } else {
 4297                     pattern <<= 1U;
 4298                 }
 4299                 }
 4300             }
 4301 
 4302             out->pattern = pattern;
 4303             } else {
 4304             int val;
 4305 
 4306             TRACE(("converting pattern id \"%s\"\n",
 4307                    fragment_to_tempstr(&item)));
 4308             if (!regis_num_to_int(&item, &val))
 4309                 val = -1;
 4310             switch (val) {  /* FIXME: exponential allowed? */
 4311             case 0:
 4312                 out->pattern = 0x00;    /* solid bg */
 4313                 break;
 4314             case 1:
 4315                 out->pattern = 0xff;    /* solid fg */
 4316                 break;
 4317             case 2:
 4318                 out->pattern = 0xf0;    /* dash */
 4319                 break;
 4320             case 3:
 4321                 out->pattern = 0xe4;    /* dash dot */
 4322                 break;
 4323             case 4:
 4324                 out->pattern = 0xaa;    /* dot */
 4325                 break;
 4326             case 5:
 4327                 out->pattern = 0xea;    /* dash dot dot */
 4328                 break;
 4329             case 6:
 4330                 out->pattern = 0x88;    /* sparse dot */
 4331                 break;
 4332             case 7:
 4333                 out->pattern = 0x84;    /* asymmetric sparse dot */
 4334                 break;
 4335             case 8:
 4336                 out->pattern = 0xc8;    /* sparse dash dot */
 4337                 break;
 4338             case 9:
 4339                 out->pattern = 0x86;    /* sparse dot dash */
 4340                 break;
 4341             default:
 4342                 TRACE(("DATA_ERROR: unknown ReGIS standard write pattern \"%d\"\n",
 4343                    val));
 4344                 return 0;
 4345             }
 4346             }
 4347 
 4348             TRACE(("final pattern is %02x\n", out->pattern));
 4349             continue;
 4350         }
 4351         skip_regis_whitespace(arg);
 4352 
 4353         TRACE(("DATA_ERROR: skipping unknown token in pattern suboption: \"%s\"\n",
 4354                fragment_to_tempstr(arg)));
 4355         pop_fragment(arg);
 4356         }
 4357     }
 4358     break;
 4359     case 'R':
 4360     case 'r':
 4361     TRACE(("write control switch to replacement writing mode \"%s\"\n",
 4362            fragment_to_tempstr(arg)));
 4363     out->write_style = WRITE_STYLE_REPLACE;
 4364     break;
 4365     case 'S':
 4366     case 's':
 4367     TRACE(("write control shading control \"%s\"\n",
 4368            fragment_to_tempstr(arg)));
 4369     {
 4370         RegisDataFragment suboptionset;
 4371         RegisDataFragment suboptionarg;
 4372         RegisDataFragment item;
 4373         char suboption;
 4374         char shading_character = '\0';
 4375         unsigned reference_dim = WRITE_SHADING_REF_Y;
 4376         /* FIXME: are relative offsets additive? */
 4377         int ref_x = cur_x, ref_y = cur_y;
 4378         int shading_enabled = 0;
 4379 
 4380         while (!fragment_consumed(arg)) {
 4381         if (skip_regis_whitespace(arg))
 4382             continue;
 4383 
 4384         if (extract_regis_string(arg, state->temp, state->templen)) {
 4385             TRACE(("found fill char \"%s\"\n", state->temp));
 4386             /* FIXME: allow longer strings, ignore extra chars, or treat as error? */
 4387             if (strlen(state->temp) != 1) {
 4388             TRACE(("DATA_ERROR: expected exactly one char in fill string FIXME\n"));
 4389             return 0;
 4390             }
 4391             shading_character = state->temp[0];
 4392             shading_enabled = 1;
 4393             TRACE(("shading character is: '%c' (%d)\n",
 4394                shading_character, (int) shading_character));
 4395             continue;
 4396         }
 4397 
 4398         if (extract_regis_parenthesized_data(arg, &suboptionset)) {
 4399             skip_regis_whitespace(&suboptionset);
 4400             TRACE(("got shading control suboptionset: \"%s\"\n",
 4401                fragment_to_tempstr(&suboptionset)));
 4402             while (!fragment_consumed(&suboptionset)) {
 4403             if (skip_regis_whitespace(&suboptionset))
 4404                 continue;
 4405             if (extract_regis_option(&suboptionset, &suboption,
 4406                          &suboptionarg)) {
 4407                 TRACE(("inspecting write shading suboption \"%c\" with value \"%s\"\n",
 4408                    suboption,
 4409                    fragment_to_tempstr(&suboptionarg)));
 4410                 switch (suboption) {
 4411                 case 'X':
 4412                 case 'x':
 4413                 TRACE(("found horizontal shading suboption \"%s\"\n",
 4414                        fragment_to_tempstr(&suboptionarg)));
 4415                 if (!fragment_consumed(&suboptionarg)) {
 4416                     TRACE(("DATA_ERROR: unexpected value to horizontal shading suboption FIXME\n"));
 4417                     return 0;
 4418                 }
 4419                 reference_dim = WRITE_SHADING_REF_X;
 4420                 shading_enabled = 1;
 4421                 break;
 4422                 default:
 4423                 TRACE(("DATA_ERROR: unknown ReGIS write pattern suboption '%c' arg \"%s\"\n",
 4424                        suboption,
 4425                        fragment_to_tempstr(&suboptionarg)));
 4426                 return 0;
 4427                 }
 4428                 continue;
 4429             }
 4430 
 4431             TRACE(("DATA_ERROR: skipping unknown token in shading control suboptionset (expecting option): \"%s\"\n",
 4432                    fragment_to_tempstr(&suboptionset)));
 4433             pop_fragment(&suboptionset);
 4434             }
 4435             continue;
 4436         }
 4437 
 4438         if (extract_regis_extent(arg, &item)) {
 4439             TRACE(("found extent in shading option curr=%d,%d ref=%d,%d\n",
 4440                cur_x, cur_y, ref_x, ref_y));
 4441             if (!load_regis_coord_extent(context,
 4442                          fragment_to_tempstr(&item),
 4443                          ref_x, ref_y,
 4444                          &ref_x, &ref_y)) {
 4445             TRACE(("DATA_ERROR: unable to parse extent in write shading option '%c': \"%s\"\n",
 4446                    option, fragment_to_tempstr(&item)));
 4447             return 0;
 4448             }
 4449             TRACE(("shading reference = %d,%d (%s)\n", ref_x, ref_y,
 4450                ((reference_dim == WRITE_SHADING_REF_X)
 4451                 ? "X"
 4452                 : "Y")));
 4453             continue;
 4454         }
 4455 
 4456         if (extract_regis_num(arg, &item)) {
 4457             if (!regis_num_to_int(&item, &shading_enabled)) {
 4458             TRACE(("DATA_ERROR: unable to parse int in write shading option '%c': \"%s\"\n",
 4459                    option, fragment_to_tempstr(&item)));
 4460             return 0;
 4461             }
 4462             if (shading_enabled < 0 || shading_enabled > 1) {
 4463             TRACE(("interpreting out of range value %d as 0 FIXME\n",
 4464                    shading_enabled));
 4465             shading_enabled = 0;
 4466             }
 4467             TRACE(("shading enabled = %d\n", shading_enabled));
 4468             continue;
 4469         }
 4470 
 4471         if (skip_regis_whitespace(arg)) {
 4472             continue;
 4473         }
 4474 
 4475         TRACE(("DATA_ERROR: skipping unknown token in shade suboption: \"%s\"\n",
 4476                fragment_to_tempstr(arg)));
 4477         pop_fragment(arg);
 4478         }
 4479 
 4480         if (shading_enabled) {
 4481         out->shading_enabled = 1U;
 4482         out->shading_reference_dim = reference_dim;
 4483         out->shading_reference = ((reference_dim == WRITE_SHADING_REF_X)
 4484                       ? ref_x
 4485                       : ref_y);
 4486         out->shading_character = shading_character;
 4487         TRACE(("final shading state: enabled, dim=%d ref=%d, char=%c\n",
 4488                out->shading_reference_dim, out->shading_reference,
 4489                out->shading_character));
 4490         } else {
 4491         /* FIXME: confirm there is no effect if shading isn't enabled
 4492          * in the same command
 4493          */
 4494         out->shading_enabled = 0U;
 4495         TRACE(("final shading state: shading disabled\n"));
 4496         }
 4497     }
 4498     break;
 4499     case 'V':
 4500     case 'v':
 4501     TRACE(("write control switch to overlay writing mode \"%s\"\n",
 4502            fragment_to_tempstr(arg)));
 4503     out->write_style = WRITE_STYLE_OVERLAY;
 4504     break;
 4505     default:
 4506     TRACE(("DATA_ERROR: ignoring unknown ReGIS write option \"%c\" arg \"%s\"\n",
 4507            option, fragment_to_tempstr(arg)));
 4508     return 0;
 4509     }
 4510 
 4511     return 1;
 4512 }
 4513 
 4514 static int
 4515 load_regis_write_control_set(RegisParseState *state,
 4516                  RegisGraphicsContext const *context,
 4517                  int cur_x, int cur_y,
 4518                  RegisDataFragment *controls,
 4519                  RegisWriteControls *out)
 4520 {
 4521     RegisDataFragment optionset;
 4522     RegisDataFragment arg;
 4523     char option;
 4524 
 4525     while (!fragment_consumed(controls)) {
 4526     if (skip_regis_whitespace(controls))
 4527         continue;
 4528 
 4529     if (extract_regis_parenthesized_data(controls, &optionset)) {
 4530         TRACE(("got write control optionset: \"%s\"\n",
 4531            fragment_to_tempstr(&optionset)));
 4532         while (!fragment_consumed(&optionset)) {
 4533         skip_regis_whitespace(&optionset);
 4534         if (extract_regis_option(&optionset, &option, &arg)) {
 4535             skip_regis_whitespace(&arg);
 4536             TRACE(("got write control option and value: \"%c\" \"%s\"\n",
 4537                option, fragment_to_tempstr(&arg)));
 4538             if (!load_regis_write_control(state, context,
 4539                           cur_x, cur_y,
 4540                           option, &arg, out)) {
 4541             return 0;
 4542             }
 4543             continue;
 4544         }
 4545 
 4546         TRACE(("DATA_ERROR: skipping unknown token in write control optionset (expecting option): \"%s\"\n",
 4547                fragment_to_tempstr(&optionset)));
 4548         pop_fragment(&optionset);
 4549         }
 4550         continue;
 4551     }
 4552 
 4553     TRACE(("DATA_ERROR: skipping unknown token in write controls (expecting optionset): \"%s\"\n",
 4554            fragment_to_tempstr(controls)));
 4555     pop_fragment(controls);
 4556     }
 4557 
 4558     return 1;
 4559 }
 4560 
 4561 static void
 4562 init_regis_write_controls(int graphics_termid, unsigned all_planes,
 4563               RegisWriteControls *controls)
 4564 {
 4565     controls->pv_multiplier = 1U;
 4566     controls->pattern = 0xff;   /* solid */
 4567     controls->pattern_multiplier = 2U;
 4568     controls->invert_pattern = 0U;
 4569     controls->plane_mask = all_planes;
 4570     controls->write_style = WRITE_STYLE_OVERLAY;
 4571     switch (graphics_termid) {
 4572     case 125:           /* FIXME: verify */
 4573     case 240:           /* FIXME: verify */
 4574     case 241:           /* FIXME: verify */
 4575     case 330:
 4576     controls->foreground = 3U;
 4577     break;
 4578     case 340:
 4579     default:
 4580     controls->foreground = 7U;
 4581     break;
 4582     case 382:
 4583     controls->foreground = 1U;  /* FIXME: verify */
 4584     break;
 4585     }
 4586     controls->shading_enabled = 0U;
 4587     controls->shading_character = '\0';
 4588     controls->shading_reference = 0;    /* no meaning if shading is disabled */
 4589     controls->shading_reference_dim = WRITE_SHADING_REF_NONE;
 4590     controls->line_width = 1U;
 4591     /* FIXME: add the rest */
 4592 }
 4593 
 4594 static void
 4595 map_regis_graphics_pages(XtermWidget xw, RegisGraphicsContext *context)
 4596 {
 4597     const int charrow = 0;
 4598     const int charcol = 0;
 4599     unsigned old_display_id = ~0U;
 4600 
 4601     if (context->destination_graphic)
 4602     context->destination_graphic->hidden = True;
 4603     if (context->display_graphic) {
 4604     context->display_graphic->hidden = True;
 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 = True;
 4616     context->destination_graphic->valid = True;
 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 = False;
 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 = True;
 4634         context->force_refresh = True;
 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 = True;
 4642     }
 4643 
 4644     TRACE(("using graphics destination=[%d -> %u] display=[%d -> %u]\n",
 4645        context->destination_page,
 4646        (context->destination_graphic
 4647         ? context->destination_graphic->id
 4648         : 0U),
 4649        context->display_page,
 4650        (context->display_graphic
 4651         ? context->display_graphic->id
 4652         : 0U)));
 4653 }
 4654 
 4655 static void
 4656 copy_regis_write_controls(RegisWriteControls const *src,
 4657               RegisWriteControls *dst)
 4658 {
 4659     dst->pv_multiplier = src->pv_multiplier;
 4660     dst->pattern = src->pattern;
 4661     dst->pattern_multiplier = src->pattern_multiplier;
 4662     dst->invert_pattern = src->invert_pattern;
 4663     dst->foreground = src->foreground;
 4664     dst->plane_mask = src->plane_mask;
 4665     dst->write_style = src->write_style;
 4666     dst->shading_enabled = src->shading_enabled;
 4667     dst->shading_character = src->shading_character;
 4668     dst->shading_reference = src->shading_reference;
 4669     dst->shading_reference_dim = src->shading_reference_dim;
 4670     dst->line_width = src->line_width;
 4671 }
 4672 
 4673 static void
 4674 init_regis_text_controls(RegisTextControls *controls)
 4675 {
 4676     controls->alphabet_num = 0U;    /* built-in */
 4677     controls->character_set_l = 0U; /* ASCII */
 4678     controls->character_set_r = 0U; /* Latin-1 */
 4679     get_standard_character_size(1, &controls->character_display_w,
 4680                 &controls->character_display_h,
 4681                 &controls->character_unit_cell_w,
 4682                 &controls->character_unit_cell_h,
 4683                 &controls->character_inc_x,
 4684                 &controls->character_inc_y);
 4685     controls->string_rotation = 0;
 4686     controls->character_rotation = 0;
 4687     controls->slant = 0;
 4688 }
 4689 
 4690 static void
 4691 copy_regis_text_controls(RegisTextControls const *src, RegisTextControls *dst)
 4692 {
 4693     dst->alphabet_num = src->alphabet_num;
 4694     dst->character_set_l = src->character_set_l;
 4695     dst->character_set_r = src->character_set_r;
 4696     dst->character_display_w = src->character_display_w;
 4697     dst->character_display_h = src->character_display_h;
 4698     dst->character_unit_cell_w = src->character_unit_cell_w;
 4699     dst->character_unit_cell_h = src->character_unit_cell_h;
 4700     dst->character_inc_x = src->character_inc_x;
 4701     dst->character_inc_y = src->character_inc_y;
 4702     dst->string_rotation = src->string_rotation;
 4703     dst->character_rotation = src->character_rotation;
 4704     dst->slant = src->slant;
 4705 }
 4706 
 4707 static void
 4708 init_regis_alphabets(RegisGraphicsContext *context)
 4709 {
 4710     unsigned alphabet_index;
 4711 
 4712     for (alphabet_index = 0U; alphabet_index < MAX_REGIS_ALPHABETS;
 4713      alphabet_index++) {
 4714     context->alphabets[alphabet_index].alphabet_num = INVALID_ALPHABET_NUM;
 4715     context->alphabets[alphabet_index].pixw = 0U;
 4716     context->alphabets[alphabet_index].pixh = 0U;
 4717     context->alphabets[alphabet_index].name[0] = '\0';
 4718     context->alphabets[alphabet_index].fontname[0] = '\0';
 4719     context->alphabets[alphabet_index].use_font = 0;
 4720     context->alphabets[alphabet_index].bytes = NULL;
 4721     }
 4722 }
 4723 
 4724 static void
 4725 init_regis_graphics_context(int graphics_termid, int width, int height,
 4726                 unsigned max_colors, const char *builtin_font,
 4727                 RegisGraphicsContext *context)
 4728 {
 4729     context->destination_graphic = NULL;
 4730     context->display_graphic = NULL;
 4731     context->display_page = 0U;
 4732     context->destination_page = 0U;
 4733     context->graphics_termid = graphics_termid;
 4734 
 4735     /* reset addressing / clear user coordinates */
 4736     context->width = width;
 4737     context->height = height;
 4738     context->x_off = 0;
 4739     context->y_off = 0;
 4740     context->x_div = width - 1;
 4741     context->y_div = height - 1;
 4742 
 4743     /*
 4744      * Generate a mask covering all valid color register address bits
 4745      * (but don't bother past 2**16).
 4746      */
 4747     context->all_planes = max_colors;
 4748     context->all_planes--;
 4749     context->all_planes |= 1U;
 4750     context->all_planes |= context->all_planes >> 1U;
 4751     context->all_planes |= context->all_planes >> 2U;
 4752     context->all_planes |= context->all_planes >> 4U;
 4753     context->all_planes |= context->all_planes >> 8U;
 4754 
 4755     context->builtin_font = builtin_font;
 4756 
 4757     init_regis_write_controls(graphics_termid, context->all_planes,
 4758                   &context->persistent_write_controls);
 4759     copy_regis_write_controls(&context->persistent_write_controls,
 4760                   &context->temporary_write_controls);
 4761 
 4762     init_regis_text_controls(&context->persistent_text_controls);
 4763     context->current_text_controls = &context->persistent_text_controls;
 4764     init_regis_alphabets(context);
 4765 
 4766     context->multi_input_mode = 0;
 4767     /* FIXME: coordinates */
 4768     /* FIXME: scrolling */
 4769     context->background = 0U;
 4770     /* FIXME: input cursor location */
 4771     /* FIXME: input cursor style */
 4772     context->graphics_output_cursor_x = 0;
 4773     context->graphics_output_cursor_y = 0;
 4774     /* FIXME: output cursor style */
 4775 
 4776     context->force_refresh = False;
 4777 }
 4778 
 4779 static int
 4780 parse_regis_command(RegisParseState *state)
 4781 {
 4782     char ch;
 4783 
 4784     if (!extract_regis_command(&state->input, &ch))
 4785     return 0;
 4786 
 4787     switch (ch) {
 4788     case 'C':
 4789     case 'c':
 4790     /* Curve
 4791 
 4792      * C
 4793      * (A)  # set the arc length in degrees (+ or nothing for
 4794      *      # counter-clockwise, - for clockwise, rounded to the
 4795      *      # closest integer degree)
 4796      * (B)  # begin closed curve sequence (must have at least two
 4797      *      # values; this option can not be nested)
 4798      * (C)  # position is the center, current location is the
 4799      *      # circumference (stays in effect until next command)
 4800      * (E)  # end curve sequence (drawing is performed here)
 4801      * (S)  # begin open curve sequence
 4802      * (W)  # temporary write options (see write command)
 4803      * [<center, circumference position>]  # center if (C), otherwise point on circumference
 4804      * [<point in curve sequence>]...  # if between (B) and (E)
 4805      * <pv>...  # if between (B) and (E)
 4806      */
 4807     TRACE(("found ReGIS command \"%c\" (curve)\n", ch));
 4808     state->command = 'c';
 4809     state->curve_mode = CURVE_POSITION_ARC_EDGE;
 4810     state->arclen = 360;
 4811     state->num_points = 0U;
 4812     break;
 4813     case 'F':
 4814     case 'f':
 4815     /* Fill
 4816 
 4817      * F
 4818      * (V)  # polygon (see vector command)
 4819      * (C)  # curve (see curve command)
 4820      * (W)  # temporary write options (see write command)
 4821      */
 4822     TRACE(("found ReGIS command \"%c\" (filled polygon)\n", ch));
 4823     state->command = 'f';
 4824     break;
 4825     case 'L':
 4826     case 'l':
 4827     /* Load
 4828 
 4829      * L
 4830      * (A)  # set alphabet number or name
 4831      * (F)"fontname"  # load from font (xterm extension)
 4832      * (S)[w,h]  # set glyph size (xterm extension)
 4833      * "ascii"xx,xx,xx,xx,xx,xx,xx,xx  # pixel values
 4834      */
 4835     TRACE(("found ReGIS command \"%c\" (load charset)\n", ch));
 4836     state->command = 'l';
 4837     break;
 4838     case 'P':
 4839     case 'p':
 4840     /* Position
 4841 
 4842      * P
 4843      * (B)  # begin bounded position stack (last point returns to first)
 4844      * (E)  # end position stack
 4845      * (P)  # select graphics page for the input and output cursors
 4846      * (S)  # begin unbounded position stack
 4847      * (W)  # temporary write options (see write command)
 4848      * <pv>  # move: 0 == right, 1 == upper right, ..., 7 == lower right
 4849      * [<position>]  # move to position (X, Y, or both)
 4850      *
 4851      * Note the stack does not need to be ended before the next command
 4852      * Note: maximum depth is 16 levels
 4853      */
 4854     TRACE(("found ReGIS command \"%c\" (position)\n", ch));
 4855     state->command = 'p';
 4856     break;
 4857     case 'R':
 4858     case 'r':
 4859     /* Report
 4860 
 4861      * R
 4862      * (E)  # parse error
 4863      * (I<val>)  # set input mode (0 == one-shot, 1 == multiple) (always returns CR)
 4864      * (L)  # current alphabet number and name
 4865      * (M(<name>)  # macrograph contents
 4866      * (M(=)  # macrograph storage (free bytes of total bytes)
 4867      * (P)  # absolute output cursor position
 4868      * (P(I))  # interactive locator mode (in one-shot or multiple mode)
 4869      * (P(I[xmul,ymul]))  # interactive locator mode with arrow key movement multipliers
 4870      */
 4871     TRACE(("found ReGIS command \"%c\" (report status)\n", ch));
 4872     state->command = 'r';
 4873     break;
 4874     case 'S':
 4875     case 's':
 4876     /* Screen
 4877 
 4878      * S
 4879      * (A[<upper left>][<lower right>])  # adjust screen coordinates
 4880      * (C<setting>  # 0 (cursor output off), 1 (cursor output on)
 4881      * (E)  # erase to background color, resets shades, curves, and stacks
 4882      * (F)  # print the graphic and erase the screen (DECprint extension)
 4883      * (H(P<printer offset>)[<print area cornet>][<print area corner>)
 4884      * (I<color register>)  # set the background to a specific register
 4885      * (I(<rgbcode>))  # set the background to the register closest to an "RGB" color
 4886      * (I(R<r>G<g>B<b>))  # set the background to the register closest to an RGB triplet (RLogin extension)
 4887      * (I(H<h>L<l>S<s>))  # set the background to the register closest to an HLS triplet
 4888      * (I(L<l>))  # set the background to the register closest to a grayscale value
 4889      * (M<color index to set>(<rgbcode>)...)  # codes are D (black), R (red), G (green), B (blue), C (cyan), Y (yellow), M (magenta), W (white) (sets color and grayscale registers)
 4890      * (M<color index to set>(A<rgbcode>)...)  # codes are D (black), R (red), G (green), B (blue), C (cyan), Y (yellow), M (magenta), W (white) (sets color registers only)
 4891      * (M<color index to set>(R<red>G<green>B<blue>)...)  # 0..100, 0..100, 0..100 (sets color and grayscale registers) (RLogin extension)
 4892      * (M<color index to set>(AR<red>G<green>B<blue>)...)  # 0..100, 0..100, 0..100 (sets color registers only) (RLogin extension)
 4893      * (M<color index to set>(H<hue>L<lightness>S<saturation>)...)  # 0..360, 0..100, 0..100 (sets color and grayscale registers)
 4894      * (M<color index to set>(AH<hue>L<lightness>S<saturation>)...)  # 0..360, 0..100, 0..100 (sets color registers only)
 4895      * (M<color index to set>(L<mono level>)...)  # level is 0 ... 100 (sets grayscale registers only)
 4896      * (P<graphics page number>)  # 0 (default) or 1
 4897      * (S(<scale>)  # scale screen output by scale (default 1, VT125:max=2, VT3x0:unsupported) FIXME
 4898      * (S(X<scale>)  # scale screen output horizontally by scale (default 1, VT125:max=2, VT3x0:unsupported) FIXME
 4899      * (S(Y<scale>)  # scale screen output vertically by scale (default 1, VT125:max=2, VT3x0:unsupported) FIXME
 4900      * (T(<time delay ticks>)  # delay (60 ticks is one second, up to 32767 ticks)
 4901      * (N<setting>)  # 0 == normal video, 1 == negative/reverse video (not supported on VT3x0)
 4902      * (W(M<factor>)  # PV multiplier
 4903      * <PV scroll offset>  # scroll data so given coordinate is at the upper-left
 4904      * [scroll offset]  # scroll data so given coordinate is at the upper-left
 4905      */
 4906     TRACE(("found ReGIS command \"%c\" (screen)\n", ch));
 4907     state->command = 's';
 4908     break;
 4909     case 'T':
 4910     case 't':
 4911     /* Text
 4912 
 4913      * T
 4914      * (A)  # specify which alphabet/font to select glyphs from (0==builtin)
 4915      * (A0L"<designator>"))  # specify a built-in set for GL via two-char designator
 4916      * (A0R"<designator>"))  # specify a built-in set for GR via two-char or three-char designator
 4917      * (A<num>R"<designator>"))  # specify a user-loaded (1-3) set for GR via two-char or three-char designator
 4918      * (B)  # begin temporary text control
 4919      * (D<char angle>)  # specify a character tilt
 4920      * (D<str angle>S<size id>)  # specify a string tilt
 4921      * (D<str angle>S<size id>D<char angle>)  # specify a string and character tilt
 4922      * (E)  # end temporary text control
 4923      * (H<factor>)  # select a height multiplier (GIGI:1-16, VT340:1-256)
 4924      * (I<angle>)  # italic/oblique: no slant (0), lean forward (-1 though -45), lean back (+1 through +45)
 4925      * (M[width factor,height factor])  # select size multipliers (width 1-16) (height 1-256)
 4926      * (S<size id>)  # select one of the 17 standard character sizes
 4927      * (S[dimensions])  # set a custom display cell size (char with border)
 4928      * (U[dimensions])  # set a custom unit cell size (char size)
 4929      * (W<write command>)  # temporary write options (see write command)
 4930      * [<char offset>]  # optional manual offset between characters
 4931      * <PV spacing>  # move half-increments for subscripts and superscripts
 4932      * '<text>'  # optional
 4933      * "<text>"  # optional
 4934      */
 4935     TRACE(("found ReGIS command \"%c\" (text)\n", ch));
 4936     state->command = 't';
 4937     state->text_tilt_state = TEXT_TILT_STATE_READY;
 4938     break;
 4939     case 'V':
 4940     case 'v':
 4941     /* Vector
 4942 
 4943      * V
 4944      * (B)  # begin bounded position stack (last point returns to first)
 4945      * (E)  # end position stack
 4946      * (S)  # begin unbounded position stack
 4947      * (W)  # temporary write options (see write command)
 4948      * <pv>  # draw a line to the pixel vector
 4949      * []  # draw a dot at the current location
 4950      * [<position>]  # draw a line to position
 4951      */
 4952     TRACE(("found ReGIS command \"%c\" (vector)\n", ch));
 4953     state->command = 'v';
 4954     break;
 4955     case 'W':
 4956     case 'w':
 4957     /* Write
 4958 
 4959      * W
 4960      * (A<setting>)  # 0 == disable alternate, 1 == enable alternate/blink FIXME
 4961      * (C)  # complement writing mode
 4962      * (E)  # erase writing mode
 4963      * (F<plane>)  # set the plane mask to control which pixel bits are updated
 4964      * (I<color register>)  # set the foreground to a specific register
 4965      * (I(<rgbcode>))  # set the foreground to the register closest to an "RGB" color
 4966      * (I(R<r>G<g>B<b>))  # set the foreground to the register closest to an RGB triplet (RLogin extension)
 4967      * (I(H<h>L<l>S<s>))  # set the foreground to the register closest to an HLS triplet
 4968      * (I(L<l>))  # set the foreground to the register closest to a grayscale value
 4969      * (L<width>)  # set the line width (RLogin extension) FIXME
 4970      * (M<pixel vector multiplier>)  # set the multiplication factor
 4971      * (N<setting>)  # 0 == negative patterns disabled, 1 == negative patterns enabled
 4972      * (P<pattern number>)  # 0..9: 0 == none, 1 == solid, 2 == 50% dash, 3 == dash-dot
 4973      * (P<pattern bits>)  # 2 to 8 bits represented as a 0/1 sequence
 4974      * (P<(M<pattern multiplier>))  # set the pattern multiplier
 4975      * (R)  # replacement writing mode
 4976      * (S'<character>')  # set shading character
 4977      * (S<setting>)  # 0 == disable shading, 1 == enable shading
 4978      * (S[reference point])  # set a horizontal reference line including this point (X ignored)
 4979      * (S(X)[reference point])  # set a vertical reference line including this point
 4980      * (V)  # overlay writing mode
 4981      */
 4982     TRACE(("found ReGIS command \"%c\" (write parameters)\n", ch));
 4983     state->command = 'w';
 4984     break;
 4985     case '@':
 4986     /* Macrograph
 4987 
 4988      * .  # clear all macrographs
 4989      * :<letter> ...@;  # define macrograph for letter
 4990      * <letter>  # expand macrograph for letter
 4991      */
 4992     TRACE(("found ReGIS macrograph command\n"));
 4993     state->command = '@';
 4994     break;
 4995     default:
 4996     TRACE(("DATA_ERROR: unknown ReGIS command %04x (%c), setting to '_'\n",
 4997            (int) ch, ch));
 4998     state->command = '_';
 4999     state->option = '_';
 5000     return 0;
 5001     }
 5002 
 5003     state->option = '_';
 5004 
 5005     return 1;
 5006 }
 5007 
 5008 static int
 5009 parse_regis_option(RegisParseState *state, RegisGraphicsContext *context)
 5010 {
 5011     RegisDataFragment optionarg;
 5012 
 5013     if (!extract_regis_option(&state->input, &state->option, &optionarg))
 5014     return 0;
 5015     skip_regis_whitespace(&optionarg);
 5016 
 5017     TRACE(("found ReGIS option '%c', parsing argument \"%s\"\n",
 5018        state->option, fragment_to_tempstr(&optionarg)));
 5019 
 5020     switch (state->command) {
 5021     case 'c':
 5022     TRACE(("inspecting curve option \"%c\" with value \"%s\"\n",
 5023            state->option, fragment_to_tempstr(&optionarg)));
 5024     switch (state->option) {
 5025     case 'A':
 5026     case 'a':
 5027         TRACE(("found arc length \"%s\"\n",
 5028            fragment_to_tempstr(&optionarg)));
 5029         {
 5030         RegisDataFragment arclen;
 5031 
 5032         if (!extract_regis_num(&optionarg, &arclen)) {
 5033             TRACE(("DATA_ERROR: expected int in curve arclen option: \"%s\"\n",
 5034                fragment_to_tempstr(&optionarg)));
 5035             break;
 5036         }
 50