1 /* This file is part of the GNU plotutils package.  Copyright (C) 1995,
2    1996, 1997, 1998, 1999, 2000, 2005, 2008, Free Software Foundation, Inc.
3 
4    The GNU plotutils package is free software.  You may redistribute it
5    and/or modify it under the terms of the GNU General Public License as
6    published by the Free Software foundation; either version 2, or (at your
7    option) any later version.
8 
9    The GNU plotutils package is distributed in the hope that it will be
10    useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12    General Public License for more details.
13 
14    You should have received a copy of the GNU General Public License along
15    with the GNU plotutils package; see the file COPYING.  If not, write to
16    the Free Software Foundation, Inc., 51 Franklin St., Fifth Floor,
17    Boston, MA 02110-1301, USA. */
18 
19 /* This file contains the internal paint_path() and paint_paths() methods,
20    which the public method endpath() is a wrapper around. */
21 
22 /* This file also contains the internal path_is_flushable() method, which
23    is invoked after any path segment is added to the segment list, provided
24    (0) the segment list has become greater than or equal to the
25    `max_unfilled_path_length' Plotter parameter, (1) the path isn't to be
26    filled.  In most Plotters, this operation simply returns true. */
27 
28 /* This file also contains the internal maybe_prepaint_segments() method.
29    It is called immediately after any segment is added to a path.  Some
30    Plotters, at least under some circumstances, treat endpath() as a no-op.
31    Instead, they plot the segments of a path in real time.  */
32 
33 /**********************************************************************/
34 
35 /* This version of paint_path() is for XDrawablePlotters (and XPlotters).
36    By construction, for such Plotters our path buffer always contains
37    either a segment list, or an ellipse object.  If it's a segment list, it
38    contains either (1) a sequence of line segments, or (2) a single
39    circular or elliptic arc segment.  Those are all sorts of path that X11
40    can handle.  (For an ellipse or circular/elliptic arc segment to have
41    been added to the path buffer, the map from user to device coordinates
42    must preserve axes.) */
43 
44 /* If the line style is "solid" and the path has zero width, it's actually
45    drawn in real time, before endpath() and paint_path() are called; see
46    the maybe_prepaint_segments() method further below in this file.  So if the
47    path doesn't need to be filled, we don't do anything in paint_path().
48    If it does, we fill it, and then redraw it. */
49 
50 /* Note that when filling a polyline, we look at
51    _plotter->drawstate->path->primitive to determine which X11 rendering
52    algorithm to use.  Our default algorithm is "Complex" (i.e. generic),
53    but when drawing polygonal approximations to ellipse and rectangle
54    primitives, which we know must be convex, we specify "Convex" instead,
55    to speed up rendering. */
56 
57 #include "sys-defines.h"
58 #include "extern.h"
59 
60 /* number of XPoint structs we can store on the stack, for speed, without
61    invoking malloc */
62 #define MAX_NUM_POINTS_ON_STACK 128
63 
64 #define DIST(p1, p2) sqrt( ((p1).x - (p2).x) * ((p1).x - (p2).x) \
65 			  + ((p1).y - (p2).y) * ((p1).y - (p2).y))
66 
67 void
_pl_x_paint_path(S___ (Plotter * _plotter))68 _pl_x_paint_path (S___(Plotter *_plotter))
69 {
70   if (_plotter->drawstate->pen_type == 0
71       && _plotter->drawstate->fill_type == 0)
72     /* nothing to draw */
73     return;
74 
75   switch ((int)_plotter->drawstate->path->type)
76     {
77     case (int)PATH_SEGMENT_LIST:
78       {
79 	bool closed;		/* not currently used */
80 	int is_a_rectangle;
81 	int i, polyline_len;
82 	plPoint p0, p1, pc;
83 	XPoint *xarray, local_xarray[MAX_NUM_POINTS_ON_STACK];
84 	bool heap_storage;
85 	double xu_last, yu_last;
86 	bool identical_user_coordinates;
87 
88 	/* sanity checks */
89 	if (_plotter->drawstate->path->num_segments == 0)/* nothing to do */
90 	  break;
91 	if (_plotter->drawstate->path->num_segments == 1) /*shouldn't happen */
92 	  break;
93 
94 	if (_plotter->drawstate->path->num_segments == 2
95 	    && _plotter->drawstate->path->segments[1].type == S_ARC)
96 	  /* segment buffer contains a single circular arc, not a polyline */
97 	  {
98 	    p0 = _plotter->drawstate->path->segments[0].p;
99 	    p1 = _plotter->drawstate->path->segments[1].p;
100 	    pc = _plotter->drawstate->path->segments[1].pc;
101 
102 	    /* use native X rendering to draw the (transformed) circular
103                arc */
104 	    _pl_x_draw_elliptic_arc (R___(_plotter) p0, p1, pc);
105 
106 	    break;
107 	  }
108 
109 	if (_plotter->drawstate->path->num_segments == 2
110 	    && _plotter->drawstate->path->segments[1].type == S_ELLARC)
111 	  /* segment buffer contains a single elliptic arc, not a polyline */
112 	  {
113 	    p0 = _plotter->drawstate->path->segments[0].p;
114 	    p1 = _plotter->drawstate->path->segments[1].p;
115 	    pc = _plotter->drawstate->path->segments[1].pc;
116 
117 	    /* use native X rendering to draw the (transformed) elliptic
118                arc */
119 	    _pl_x_draw_elliptic_arc_2 (R___(_plotter) p0, p1, pc);
120 
121 	    break;
122 	  }
123 
124 	/* neither of above applied, so segment buffer contains a polyline,
125 	   not an arc */
126 
127 	if ((_plotter->drawstate->path->num_segments >= 3)/*check for closure*/
128 	    && (_plotter->drawstate->path->segments[_plotter->drawstate->path->num_segments - 1].p.x == _plotter->drawstate->path->segments[0].p.x)
129 	    && (_plotter->drawstate->path->segments[_plotter->drawstate->path->num_segments - 1].p.y == _plotter->drawstate->path->segments[0].p.y))
130 	  closed = true;
131 	else
132 	  closed = false;	/* 2-point ones should be open */
133 
134 	/* Check whether we `pre-drew' the polyline, i.e., drew every line
135 	   segment in real time.  (See the maybe_prepaint_segments() method
136 	   further below, which is invoked to do that.)  Our convention: we
137 	   pre-draw only if pen width is zero, and line style is "solid".
138 	   Also, we don't do it if we're drawing a polygonalized built-in
139 	   object (i.e. a rectangle or ellipse).
140 
141 	   If we pre-drew, we don't do anything here unless there's filling
142 	   to be done.  If so, we'll fill the polyline and re-edge it.  */
143 
144 	if ((_plotter->drawstate->pen_type != 0 /* pen is present */
145 	     && _plotter->drawstate->line_type == PL_L_SOLID
146 	     && !_plotter->drawstate->dash_array_in_effect /* really solid */
147 	     && _plotter->drawstate->points_are_connected /* really, really */
148 	     && _plotter->drawstate->quantized_device_line_width == 0
149 	     && !_plotter->drawstate->path->primitive) /* not builtin object */
150 	  /* we pre-drew */
151 	    &&
152 	    _plotter->drawstate->fill_type == 0)
153 	  /* there's no filling to be done, so we're out of here */
154 	  break;
155 
156 	/* At this point we know that we didn't pre-draw, or we did, but
157 	   the polyline was drawn unfilled and it'll need to be re-drawn as
158 	   filled. */
159 
160 	/* prepare an array of XPoint structures (X11 uses short ints for
161 	   these) */
162 	if (_plotter->drawstate->path->num_segments
163 	    <= MAX_NUM_POINTS_ON_STACK)
164 	  /* store XPoints on stack, for speed */
165 	  {
166 	    xarray = local_xarray;
167 	    heap_storage = false;
168 	  }
169 	else
170 	  /* store XPoints in heap */
171 	  {
172 	    xarray = (XPoint *)_pl_xmalloc (_plotter->drawstate->path->num_segments * sizeof(XPoint));
173 	    heap_storage = true;
174 	  }
175 
176 	/* convert vertices to device coordinates, removing runs; also keep
177 	   track of whether or not all points in user space are the same */
178 
179 	polyline_len = 0;
180 	xu_last = 0.0;
181 	yu_last = 0.0;
182 	identical_user_coordinates = true;
183 	for (i = 0; i < _plotter->drawstate->path->num_segments; i++)
184 	  {
185 	    plPathSegment datapoint;
186 	    double xu, yu, xd, yd;
187 	    int device_x, device_y;
188 
189 	    datapoint = _plotter->drawstate->path->segments[i];
190 	    xu = datapoint.p.x;
191 	    yu = datapoint.p.y;
192 	    xd = XD(xu, yu);
193 	    yd = YD(xu, yu);
194 	    device_x = IROUND(xd);
195 	    device_y = IROUND(yd);
196 
197 	    if (X_OOB_INT(device_x) || X_OOB_INT(device_y))
198 	      /* point position can't be represented using X11's 2-byte
199 		 ints, so truncate the polyline right here */
200 	      {
201 		_plotter->warning (R___(_plotter)
202 				   "truncating a polyline that extends too far for X11");
203 		break;
204 	      }
205 
206 	    if (i > 0 && (xu != xu_last || yu != yu_last))
207 	      /* in user space, not all points are the same */
208 	      identical_user_coordinates = false;
209 
210 	    if ((polyline_len == 0)
211 		|| (device_x != xarray[polyline_len-1].x)
212 		|| (device_y != xarray[polyline_len-1].y))
213 	      /* add point, in integer X coordinates, to the array */
214 	      {
215 		xarray[polyline_len].x = device_x;
216 		xarray[polyline_len].y = device_y;
217 		polyline_len++;
218 
219 		if (polyline_len >= _plotter->x_max_polyline_len)
220 		  /* polyline is getting too long for the X server to
221 		     handle (we determined the maximum X request size when
222 		     openpl() was invoked), so truncate it right here */
223 		  {
224 		    _plotter->warning (R___(_plotter)
225 				       "truncating a polyline that's too long for the X display");
226 		    break;
227 		  }
228 	      }
229 
230 	    xu_last = xu;
231 	    yu_last = yu;
232 	  }
233 
234 	/* Is this path a rectangle in device space?  We check this because
235 	   by calling XFillRectangle (and XDrawRectangle too, if the edging
236 	   is solid), we can save a bit of time, or at least network
237 	   bandwidth. */
238 
239 	/* N.B. This checks only for rectangles traced counterclockwise
240 	   from the lower left corner.  Should improve this. */
241 
242 #define IS_A_RECTANGLE(len,q) \
243 ((len) == 5 && (q)[0].x == (q)[4].x && (q)[0].y == (q)[4].y && \
244 (q)[0].x == (q)[3].x && (q)[1].x == (q)[2].x && \
245 (q)[0].y == (q)[1].y && (q)[2].y == (q)[3].y && \
246 (q)[0].x < (q)[1].x && (q)[0].y > (q)[2].y) /* note flipped-y convention */
247 
248 	is_a_rectangle = IS_A_RECTANGLE(polyline_len, xarray);
249 
250 	/* N.B.  If a rectangle, upper left corner = q[3], and also, width
251 	   = q[1].x - q[0].x and height = q[0].y - q[2].y (note flipped-y
252 	   convention). */
253 
254 	/* compute the square size, and offset of upper left vertex from
255 	   center of square, that we'll use when handling the notorious
256 	   special case: all user-space points in the polyline get mapped
257 	   to a single integer X pixel */
258 
259 	/* FIRST TASK: fill the polygon (if necessary). */
260 
261 	if (_plotter->drawstate->fill_type)
262 	  /* not transparent, so fill the path */
263 	  {
264 	    int x_polygon_type
265 	      = (_plotter->drawstate->path->primitive ? Convex : Complex);
266 
267 	    /* update GC used for filling */
268 	    _pl_x_set_attributes (R___(_plotter) X_GC_FOR_FILLING);
269 
270 	    /* select fill color as foreground color in GC used for filling */
271 	    _pl_x_set_fill_color (S___(_plotter));
272 
273 	    if (_plotter->x_double_buffering != X_DBL_BUF_NONE)
274 	      {
275 		if (_plotter->drawstate->path->num_segments > 1
276 		      && polyline_len == 1)
277 		  /* special case: all user-space points in the polyline
278 		     were mapped to a single integer X pixel */
279 		  XDrawPoint (_plotter->x_dpy, _plotter->x_drawable3,
280 			      _plotter->drawstate->x_gc_fill,
281 			      (int)(xarray[0].x), (int)(xarray[0].y));
282 		else
283 		  /* general case */
284 		  {
285 		    if (is_a_rectangle)
286 		      /* call XFillRectangle, for speed */
287 		      XFillRectangle (_plotter->x_dpy, _plotter->x_drawable3,
288 				      _plotter->drawstate->x_gc_fill,
289 				      (int)(xarray[3].x), (int)(xarray[3].y),
290 				      (unsigned int)(xarray[1].x - xarray[0].x),
291 				      /* flipped y */
292 				      (unsigned int)(xarray[0].y - xarray[2].y));
293 		    else
294 		      /* not a rectangle, call XFillPolygon */
295 		      XFillPolygon (_plotter->x_dpy, _plotter->x_drawable3,
296 				    _plotter->drawstate->x_gc_fill,
297 				    xarray, polyline_len,
298 				    x_polygon_type, CoordModeOrigin);
299 		  }
300 	      }
301 	    else		/* not double buffering, no `x_drawable3' */
302 	      {
303 		if (_plotter->drawstate->path->num_segments > 1
304 		      && polyline_len == 1)
305 		  /* special case: all user-space points in the polyline
306 		     were mapped to a single integer X pixel. */
307 		  {
308 		    if (_plotter->x_drawable1)
309 		      XDrawPoint (_plotter->x_dpy, _plotter->x_drawable1,
310 				  _plotter->drawstate->x_gc_fill,
311 				  (int)(xarray[0].x), (int)(xarray[0].y));
312 		    if (_plotter->x_drawable2)
313 		      XDrawPoint (_plotter->x_dpy, _plotter->x_drawable2,
314 				  _plotter->drawstate->x_gc_fill,
315 				  (int)(xarray[0].x), (int)(xarray[0].y));
316 		  }
317 		else
318 		  /* general case */
319 		  {
320 		    if (is_a_rectangle)
321 		      /* call XFillRectangle, for speed */
322 		      {
323 			if (_plotter->x_drawable1)
324 			  XFillRectangle (_plotter->x_dpy, _plotter->x_drawable1,
325 					  _plotter->drawstate->x_gc_fill,
326 					  (int)(xarray[3].x), (int)(xarray[3].y),
327 					  (unsigned int)(xarray[1].x - xarray[0].x),
328 					  /* flipped y */
329 					  (unsigned int)(xarray[0].y - xarray[2].y));
330 			if (_plotter->x_drawable2)
331 			  XFillRectangle (_plotter->x_dpy, _plotter->x_drawable2,
332 					  _plotter->drawstate->x_gc_fill,
333 					  (int)(xarray[3].x), (int)(xarray[3].y),
334 					  (unsigned int)(xarray[1].x - xarray[0].x),
335 					  /* flipped y */
336 					  (unsigned int)(xarray[0].y - xarray[2].y));
337 		      }
338 		    else
339 		      /* not a rectangle, call XFillPolygon */
340 		      {
341 			if (_plotter->x_drawable1)
342 			  XFillPolygon (_plotter->x_dpy, _plotter->x_drawable1,
343 					_plotter->drawstate->x_gc_fill,
344 					xarray, polyline_len,
345 					x_polygon_type, CoordModeOrigin);
346 			if (_plotter->x_drawable2)
347 			  XFillPolygon (_plotter->x_dpy, _plotter->x_drawable2,
348 					_plotter->drawstate->x_gc_fill,
349 					xarray, polyline_len,
350 					x_polygon_type, CoordModeOrigin);
351 		      }
352 		  }
353 	      }
354 	  }
355 
356 	/* SECOND TASK: edge the polygon (if necessary). */
357 
358 	if (_plotter->drawstate->pen_type)
359 	  /* pen is present, so edge the path */
360 	  {
361 	    int xloc = 0, yloc = 0;
362 	    unsigned int sp_size = 1;
363 
364 	    /* update GC used for drawing */
365 	    _pl_x_set_attributes (R___(_plotter) X_GC_FOR_DRAWING);
366 
367 	    /* select pen color as foreground color in GC used for drawing */
368 	    _pl_x_set_pen_color (S___(_plotter));
369 
370 	    /* Check first for the special case: all points in the polyline
371 	       were mapped to a single integer X pixel.  If (1) they
372 	       weren't all the same to begin with, or (2) they were all the
373 	       same to begin with and the cap mode is "round", then draw as
374 	       a filled circle of diameter equal to the line width;
375 	       otherwise draw nothing.  (If the circle would have diameter
376 	       1 or less, we draw a point instead.) */
377 
378 	    if (_plotter->drawstate->path->num_segments > 1
379 		&& polyline_len == 1)
380 	      /* this is the special case, so compute quantities needed for
381 		 drawing the filled circle */
382 	      {
383 		int sp_offset;
384 
385 		sp_size = (unsigned int)_plotter->drawstate->quantized_device_line_width;
386 		if (sp_size == 0)
387 		  sp_size = 1;
388 		sp_offset = (_plotter->drawstate->quantized_device_line_width + 1) / 2;
389 		xloc = xarray[0].x - sp_offset;
390 		yloc = xarray[0].y - sp_offset;
391 	      }
392 
393 	    if (_plotter->x_double_buffering != X_DBL_BUF_NONE)
394 	      /* double buffering, have a `x_drawable3' to draw into */
395 	      {
396 		if (_plotter->drawstate->path->num_segments > 1
397 		    && polyline_len == 1)
398 		  /* special case */
399 		  {
400 		    if (identical_user_coordinates == false
401 			|| _plotter->drawstate->cap_type == PL_CAP_ROUND)
402 		      {
403 			if (sp_size == 1)
404 			  /* subcase: just draw a point */
405 			  XDrawPoint (_plotter->x_dpy, _plotter->x_drawable3,
406 				      _plotter->drawstate->x_gc_fg,
407 				      (int)(xarray[0].x), (int)(xarray[0].y));
408 			else
409 			  /* draw filled circle */
410 			  XFillArc(_plotter->x_dpy, _plotter->x_drawable3,
411 				   _plotter->drawstate->x_gc_fg,
412 				   xloc, yloc, sp_size, sp_size,
413 				   0, 64 * 360);
414 		      }
415 		  }
416 		else
417 		  /* general case */
418 		  /* NOTE: this code is what libplot uses to draw nearly all
419 		     polylines, in the case when double buffering is used */
420 		  {
421 		    if (is_a_rectangle
422 			&& _plotter->drawstate->dash_array_in_effect == false
423 			&& _plotter->drawstate->line_type == PL_L_SOLID)
424 		      /* call XDrawRectangle, for speed */
425 		      XDrawRectangle (_plotter->x_dpy, _plotter->x_drawable3,
426 				      _plotter->drawstate->x_gc_fg,
427 				      (int)(xarray[3].x), (int)(xarray[3].y),
428 				      (unsigned int)(xarray[1].x - xarray[0].x),
429 				      /* flipped y */
430 				      (unsigned int)(xarray[0].y - xarray[2].y));
431 		    else
432 		      /* can't call XDrawRectangle */
433 		      XDrawLines (_plotter->x_dpy, _plotter->x_drawable3,
434 				  _plotter->drawstate->x_gc_fg,
435 				  xarray, polyline_len,
436 				  CoordModeOrigin);
437 		  }
438 	      }
439 	    else
440 	      /* not double buffering, have no `x_drawable3' */
441 	      {
442 		if (_plotter->drawstate->path->num_segments > 1
443 		    && polyline_len == 1)
444 		  /* special case */
445 		  {
446 		    if (identical_user_coordinates == false
447 			|| _plotter->drawstate->cap_type == PL_CAP_ROUND)
448 		      {
449 			if (sp_size == 1)
450 			  /* subcase: just draw a point */
451 			  {
452 			    if (_plotter->x_drawable1)
453 			      XDrawPoint (_plotter->x_dpy, _plotter->x_drawable1,
454 					  _plotter->drawstate->x_gc_fg,
455 					  (int)(xarray[0].x), (int)(xarray[0].y));
456 			    if (_plotter->x_drawable2)
457 			      XDrawPoint (_plotter->x_dpy, _plotter->x_drawable2,
458 					  _plotter->drawstate->x_gc_fg,
459 					  (int)(xarray[0].x), (int)(xarray[0].y));
460 			  }
461 			else
462 			  /* draw filled circle */
463 			  {
464 			    if (_plotter->x_drawable1)
465 			      XFillArc(_plotter->x_dpy, _plotter->x_drawable1,
466 				       _plotter->drawstate->x_gc_fg,
467 				       xloc, yloc, sp_size, sp_size,
468 				       0, 64 * 360);
469 			    if (_plotter->x_drawable2)
470 			      XFillArc(_plotter->x_dpy, _plotter->x_drawable2,
471 				       _plotter->drawstate->x_gc_fg,
472 				       xloc, yloc, sp_size, sp_size,
473 				       0, 64 * 360);
474 			}
475 		      }
476 		  }
477 		else
478 		  /* general case */
479 		  /* NOTE: this code is what libplot uses to draw nearly all
480 		     polylines; at least, if double buffering is not used */
481 		  {
482 		    if (is_a_rectangle
483 			&& _plotter->drawstate->dash_array_in_effect == false
484 			&& _plotter->drawstate->line_type == PL_L_SOLID)
485 		      /* call XDrawRectangle, for speed */
486 		      {
487 			if (_plotter->x_drawable1)
488 			  XDrawRectangle (_plotter->x_dpy, _plotter->x_drawable1,
489 					  _plotter->drawstate->x_gc_fg,
490 					  (int)(xarray[3].x), (int)(xarray[3].y),
491 					  (unsigned int)(xarray[1].x - xarray[0].x),
492 					  /* flipped y */
493 					  (unsigned int)(xarray[0].y - xarray[2].y));
494 			if (_plotter->x_drawable2)
495 			  XDrawRectangle (_plotter->x_dpy, _plotter->x_drawable2,
496 					  _plotter->drawstate->x_gc_fg,
497 					  (int)(xarray[3].x), (int)(xarray[3].y),
498 					  (unsigned int)(xarray[1].x - xarray[0].x),
499 					  /* flipped y */
500 					  (unsigned int)(xarray[0].y - xarray[2].y));
501 		      }
502 		    else
503 		      /* can't use XDrawRectangle() */
504 		      {
505 			if (_plotter->x_drawable1)
506 			  XDrawLines (_plotter->x_dpy, _plotter->x_drawable1,
507 				      _plotter->drawstate->x_gc_fg,
508 				      xarray, polyline_len,
509 				      CoordModeOrigin);
510 			if (_plotter->x_drawable2)
511 			  XDrawLines (_plotter->x_dpy, _plotter->x_drawable2,
512 				      _plotter->drawstate->x_gc_fg,
513 				      xarray, polyline_len,
514 				      CoordModeOrigin);
515 		      }
516 		  }
517 	      }
518 	  }
519 
520 	/* reset buffer used for array of XPoint structs */
521 	if (_plotter->drawstate->path->num_segments > 0)
522 	  {
523 	    if (heap_storage)
524 	      free (xarray);		/* free malloc'd array of XPoints */
525 	  }
526       }
527       break;
528 
529     case (int)PATH_ELLIPSE:
530       {
531 	int ninetymult;
532 	int x_orientation, y_orientation;
533 	int xorigin, yorigin;
534 	unsigned int squaresize_x, squaresize_y;
535 	plPoint pc;
536 	double rx, ry, angle;
537 
538 	pc = _plotter->drawstate->path->pc;
539 	rx = _plotter->drawstate->path->rx;
540 	ry = _plotter->drawstate->path->ry;
541 	angle = _plotter->drawstate->path->angle;
542 
543 	/* if angle is multiple of 90 degrees, modify to permit use of
544 	   X11 arc rendering */
545 	ninetymult = IROUND(angle / 90.0);
546 	if (angle == (double) (90 * ninetymult))
547 	  {
548 	    angle = 0.0;
549 	    if (ninetymult % 2)
550 	      {
551 		double temp;
552 
553 		temp = rx;
554 		rx = ry;
555 		ry = temp;
556 	      }
557 	  }
558 
559 	rx = (rx < 0.0 ? -rx : rx);	/* avoid obscure libxmi problems */
560 	ry = (ry < 0.0 ? -ry : ry);
561 
562 	/* axes flipped? (by default y-axis is, due to libxmi's flipped-y
563            convention) */
564 	x_orientation = (_plotter->drawstate->transform.m[0] >= 0 ? 1 : -1);
565 	y_orientation = (_plotter->drawstate->transform.m[3] >= 0 ? 1 : -1);
566 
567 	/* location of `origin' (upper left corner of bounding rect. for
568 	   ellipse) and width and height; X11's flipped-y convention
569 	   affects these values */
570 	xorigin = IROUND(XD(pc.x - x_orientation * rx,
571 			    pc.y - y_orientation * ry));
572 	yorigin = IROUND(YD(pc.x - x_orientation * rx,
573 			    pc.y - y_orientation * ry));
574 	squaresize_x = (unsigned int)IROUND(XDV(2 * x_orientation * rx, 0.0));
575 	squaresize_y = (unsigned int)IROUND(YDV(0.0, 2 * y_orientation * ry));
576 	/* Because this ellipse object was added to the path buffer, we
577 	   already know that (1) the user->device frame map preserves
578 	   coordinate axes, (2) effectively, angle == 0.  These are
579 	   necessary for the libxmi scan-conversion module to do the
580 	   drawing. */
581 
582 	/* draw ellipse (elliptic arc aligned with the coordinate axes, arc
583 	   range = 64*360 64'ths of a degree) */
584 	_pl_x_draw_elliptic_arc_internal (R___(_plotter)
585 				       xorigin, yorigin,
586 				       squaresize_x, squaresize_y,
587 				       0, 64 * 360);
588       }
589       break;
590 
591     default:			/* shouldn't happen */
592       break;
593     }
594 
595   /* maybe flush X output buffer and handle X events (a no-op for
596      XDrawablePlotters, which is overridden for XPlotters) */
597   _maybe_handle_x_events (S___(_plotter));
598 }
599 
600 /* Use native X rendering to draw what would be a circular arc in the user
601    frame on an X display.  If this is called, the map from user to device
602    coordinates is assumed to preserve coordinate axes (it may be
603    anisotropic [x and y directions scaled differently], and it may include
604    a reflection through either or both axes).  So it will be a circular or
605    elliptic arc in the device frame, of the sort that X11 supports. */
606 
607 void
_pl_x_draw_elliptic_arc(R___ (Plotter * _plotter)plPoint p0,plPoint p1,plPoint pc)608 _pl_x_draw_elliptic_arc (R___(Plotter *_plotter) plPoint p0, plPoint p1, plPoint pc)
609 {
610   double radius;
611   double theta0, theta1;
612   int startangle, anglerange;
613   int x_orientation, y_orientation;
614   int xorigin, yorigin;
615   unsigned int squaresize_x, squaresize_y;
616 
617   /* axes flipped? (by default y-axis is, due to  X's flipped-y convention) */
618   x_orientation = (_plotter->drawstate->transform.m[0] >= 0 ? 1 : -1);
619   y_orientation = (_plotter->drawstate->transform.m[3] >= 0 ? 1 : -1);
620 
621   /* radius of circular arc in user frame is distance to p0, and also to p1 */
622   radius = DIST(pc, p0);
623 
624   /* location of `origin' (upper left corner of bounding rect. on display)
625      and width and height; X's flipped-y convention affects these values */
626   xorigin = IROUND(XD(pc.x - x_orientation * radius,
627 		      pc.y - y_orientation * radius));
628   yorigin = IROUND(YD(pc.x - x_orientation * radius,
629 		      pc.y - y_orientation * radius));
630   squaresize_x = (unsigned int)IROUND(XDV(2 * x_orientation * radius, 0.0));
631   squaresize_y = (unsigned int)IROUND(YDV(0.0, 2 * y_orientation * radius));
632 
633   theta0 = _xatan2 (-y_orientation * (p0.y - pc.y),
634 		    x_orientation * (p0.x - pc.x)) / M_PI;
635   theta1 = _xatan2 (-y_orientation * (p1.y - pc.y),
636 		    x_orientation * (p1.x - pc.x)) / M_PI;
637 
638   if (theta1 < theta0)
639     theta1 += 2.0;		/* adjust so that difference > 0 */
640   if (theta0 < 0.0)
641     {
642       theta0 += 2.0;		/* adjust so that startangle > 0 */
643       theta1 += 2.0;
644     }
645 
646   if (theta1 - theta0 > 1.0)	/* swap if angle appear to be > 180 degrees */
647     {
648       double tmp;
649 
650       tmp = theta0;
651       theta0 = theta1;
652       theta1 = tmp;
653       theta1 += 2.0;		/* adjust so that difference > 0 */
654     }
655 
656   if (theta0 >= 2.0 && theta1 >= 2.0)
657     /* avoid obscure X bug */
658     {
659       theta0 -= 2.0;
660       theta1 -= 2.0;
661     }
662 
663   startangle = IROUND(64 * theta0 * 180.0); /* in 64'ths of a degree */
664   anglerange = IROUND(64 * (theta1 - theta0) * 180.0); /* likewise */
665 
666   _pl_x_draw_elliptic_arc_internal (R___(_plotter)
667 				 xorigin, yorigin,
668 				 squaresize_x, squaresize_y,
669 				 startangle, anglerange);
670 }
671 
672 /* Use native X rendering to draw what would be a quarter-ellipse in the
673    user frame on an X display.  If this is called, the map from user to
674    device coordinates is assumed to preserve coordinate axes (it may be
675    anisotropic [x and y directions scaled differently], and it may include
676    a reflection through either or both axes).  So it will be a
677    quarter-ellipse in the device frame, of the sort that the X11 drawing
678    protocol supports. */
679 void
_pl_x_draw_elliptic_arc_2(R___ (Plotter * _plotter)plPoint p0,plPoint p1,plPoint pc)680 _pl_x_draw_elliptic_arc_2 (R___(Plotter *_plotter) plPoint p0, plPoint p1, plPoint pc)
681 {
682   double rx, ry;
683   double x0, y0, x1, y1, xc, yc;
684   int startangle, endangle, anglerange;
685   int x_orientation, y_orientation;
686   int xorigin, yorigin;
687   unsigned int squaresize_x, squaresize_y;
688 
689   /* axes flipped? (by default y-axis is, due to  X's flipped-y convention) */
690   x_orientation = (_plotter->drawstate->transform.m[0] >= 0 ? 1 : -1);
691   y_orientation = (_plotter->drawstate->transform.m[3] >= 0 ? 1 : -1);
692 
693   xc = pc.x, yc = pc.y;
694   x0 = p0.x, y0 = p0.y;
695   x1 = p1.x, y1 = p1.y;
696 
697   if (y0 == yc && x1 == xc)
698     /* initial pt. on x-axis, final pt. on y-axis */
699     {
700       /* semi-axes in user frame */
701       rx = (x0 > xc) ? x0 - xc : xc - x0;
702       ry = (y1 > yc) ? y1 - yc : yc - y1;
703       /* starting and ending angles; note flipped-y convention */
704       startangle = ((x0 > xc ? 1 : -1) * x_orientation == 1) ? 0 : 180;
705       endangle = ((y1 > yc ? 1 : -1) * y_orientation == -1) ? 90 : 270;
706     }
707   else
708     /* initial pt. on y-axis, final pt. on x-axis */
709     {
710       /* semi-axes in user frame */
711       rx = (x1 > xc) ? x1 - xc : xc - x1;
712       ry = (y0 > yc) ? y0 - yc : yc - y0;
713       /* starting and ending angles; note flipped-y convention */
714       startangle = ((y0 > yc ? 1 : -1) * y_orientation == -1) ? 90 : 270;
715       endangle = ((x1 > xc ? 1 : -1) * x_orientation == 1) ? 0 : 180;
716     }
717 
718   if (endangle < startangle)
719     endangle += 360;
720   anglerange = endangle - startangle; /* always 90 or 270 */
721 
722   /* our convention: a quarter-ellipse can only be 90 degrees
723      of an X ellipse, not 270 degrees, so interchange points */
724   if (anglerange == 270)
725     {
726       int tmp;
727 
728       tmp = startangle;
729       startangle = endangle;
730       endangle = tmp;
731       anglerange = 90;
732     }
733 
734   if (startangle >= 360)
735     /* avoid obscure X bug */
736     startangle -= 360;		/* endangle no longer relevant */
737 
738   /* location of `origin' (upper left corner of bounding rect. on display)
739      and width and height; X's flipped-y convention affects these values */
740   xorigin = IROUND(XD(xc - x_orientation * rx,
741 		      yc - y_orientation * ry));
742   yorigin = IROUND(YD(xc - x_orientation * rx,
743 		      yc - y_orientation * ry));
744   squaresize_x = (unsigned int)IROUND(XDV(2 * x_orientation * rx, 0.0));
745   squaresize_y = (unsigned int)IROUND(YDV(0.0, 2 * y_orientation * ry));
746 
747   /* reexpress in 64'ths of a degree (X11 convention) */
748   startangle *= 64;
749   anglerange *= 64;
750 
751   _pl_x_draw_elliptic_arc_internal (R___(_plotter)
752 				 xorigin, yorigin,
753 				 squaresize_x, squaresize_y,
754 				 startangle, anglerange);
755 }
756 
757 /* Use native X rendering to draw an elliptic arc on an X display.  Takes
758    account of the possible presence of more than one drawable, and the
759    possible need for filling.
760 
761    The cases squaresize_{x,y} <= 1 are handled specially, since XFillArc()
762    and XDrawArc() don't support them in the way we wish.  More accurately,
763    they don't support squaresize_{x,y} = 0 (documented), and don't support
764    squaresize_{x,y} = 1 in the way we'd like (undocumented). */
765 
766 void
_pl_x_draw_elliptic_arc_internal(R___ (Plotter * _plotter)int xorigin,int yorigin,unsigned int squaresize_x,unsigned int squaresize_y,int startangle,int anglerange)767 _pl_x_draw_elliptic_arc_internal (R___(Plotter *_plotter) int xorigin, int yorigin, unsigned int squaresize_x, unsigned int squaresize_y, int startangle, int anglerange)
768 {
769   if (X_OOB_INT(xorigin) || X_OOB_INT(yorigin) || X_OOB_UNSIGNED(squaresize_x)
770       || X_OOB_UNSIGNED(squaresize_y))
771     /* dimensions can't be represented using X11's 2-byte ints, so punt */
772     {
773       _plotter->warning (R___(_plotter)
774 			 "not drawing an arc that extends too far for X11");
775       return;
776     }
777 
778   if (_plotter->drawstate->fill_type)
779     /* not transparent, so fill the arc */
780     {
781       /* update GC used for filling */
782       _pl_x_set_attributes (R___(_plotter) X_GC_FOR_FILLING);
783 
784       /* select fill color as foreground color in GC used for filling */
785       _pl_x_set_fill_color (S___(_plotter));
786 
787       if (squaresize_x <= 1 || squaresize_y <= 1)
788 	/* a special case, which XFillArc() doesn't handle in the way we'd
789 	   like; just paint a single pixel, irrespective of angle range */
790 	{
791 	  if (_plotter->x_double_buffering != X_DBL_BUF_NONE)
792 	    XDrawPoint (_plotter->x_dpy, _plotter->x_drawable3,
793 			_plotter->drawstate->x_gc_fill,
794 			xorigin, yorigin);
795 	  else
796 	    {
797 	      if (_plotter->x_drawable1)
798 		XDrawPoint (_plotter->x_dpy, _plotter->x_drawable1,
799 			    _plotter->drawstate->x_gc_fill,
800 			    xorigin, yorigin);
801 	      if (_plotter->x_drawable2)
802 		XDrawPoint (_plotter->x_dpy, _plotter->x_drawable2,
803 			    _plotter->drawstate->x_gc_fill,
804 			    xorigin, yorigin);
805 	    }
806 	}
807       else
808 	/* default case, almost always used */
809 	{
810 	  if (_plotter->x_double_buffering != X_DBL_BUF_NONE)
811 	    XFillArc(_plotter->x_dpy, _plotter->x_drawable3,
812 		     _plotter->drawstate->x_gc_fill,
813 		     xorigin, yorigin, squaresize_x, squaresize_y,
814 		     startangle, anglerange);
815 	  else
816 	    {
817 	      if (_plotter->x_drawable1)
818 		XFillArc(_plotter->x_dpy, _plotter->x_drawable1,
819 			 _plotter->drawstate->x_gc_fill,
820 			 xorigin, yorigin, squaresize_x, squaresize_y,
821 			 startangle, anglerange);
822 	      if (_plotter->x_drawable2)
823 		XFillArc(_plotter->x_dpy, _plotter->x_drawable2,
824 			 _plotter->drawstate->x_gc_fill,
825 			 xorigin, yorigin, squaresize_x, squaresize_y,
826 			 startangle, anglerange);
827 	    }
828 	}
829     }
830 
831   if (_plotter->drawstate->pen_type)
832     /* pen is present, so edge the arc */
833     {
834       unsigned int sp_size = 0;	/* keep compiler happy */
835 
836       /* update GC used for drawing */
837       _pl_x_set_attributes (R___(_plotter) X_GC_FOR_DRAWING);
838 
839       /* select pen color as foreground color in GC used for drawing */
840       _pl_x_set_pen_color (S___(_plotter));
841 
842       if (squaresize_x <= 1 || squaresize_y <= 1)
843 	/* Won't call XDrawArc in the usual way, because it performs poorly
844            when one of these two is zero, at least.  Irrespective of angle
845            range, will fill a disk of diameter equal to line width */
846 	{
847 	  int sp_offset;
848 
849 	  sp_size
850 	    = (unsigned int)_plotter->drawstate->quantized_device_line_width;
851 	  sp_offset
852 	    = (int)(_plotter->drawstate->quantized_device_line_width + 1) / 2;
853 
854 	  if (sp_size == 0)
855 	    sp_size = 1;
856 	  xorigin -= sp_offset;
857 	  yorigin -= sp_offset;
858 	}
859 
860       if (squaresize_x <= 1 || squaresize_y <= 1)
861 	/* special case */
862 	{
863 	  if (sp_size == 1)
864 	    /* special subcase: line width is small too, so just paint a
865 	       single pixel rather than filling abovementioned disk */
866 	    {
867 	      if (_plotter->x_double_buffering != X_DBL_BUF_NONE)
868 		XDrawPoint (_plotter->x_dpy, _plotter->x_drawable3,
869 			    _plotter->drawstate->x_gc_fg,
870 			    xorigin, yorigin);
871 	      else
872 		{
873 		  if (_plotter->x_drawable1)
874 		    XDrawPoint (_plotter->x_dpy, _plotter->x_drawable1,
875 				_plotter->drawstate->x_gc_fg,
876 				xorigin, yorigin);
877 		  if (_plotter->x_drawable2)
878 		    XDrawPoint (_plotter->x_dpy, _plotter->x_drawable2,
879 				_plotter->drawstate->x_gc_fg,
880 				xorigin, yorigin);
881 		}
882 	    }
883 	  else
884 	    /* normal version of special case: fill a disk of diameter
885 	       equal to line width */
886 	    {
887 	      if (_plotter->x_double_buffering != X_DBL_BUF_NONE)
888 		XFillArc(_plotter->x_dpy, _plotter->x_drawable3,
889 			 _plotter->drawstate->x_gc_fg,
890 			 xorigin, yorigin, sp_size, sp_size,
891 			 0, 64 * 360);
892 	      else
893 		{
894 		  if (_plotter->x_drawable1)
895 		    XFillArc(_plotter->x_dpy, _plotter->x_drawable1,
896 			     _plotter->drawstate->x_gc_fg,
897 			     xorigin, yorigin, sp_size, sp_size,
898 			     0, 64 * 360);
899 		  if (_plotter->x_drawable2)
900 		    XFillArc(_plotter->x_dpy, _plotter->x_drawable2,
901 			     _plotter->drawstate->x_gc_fg,
902 			     xorigin, yorigin, sp_size, sp_size,
903 			     0, 64 * 360);
904 		}
905 	    }
906 	}
907       else
908 	/* default case, which is what is almost always used */
909 	{
910 	  if (_plotter->x_double_buffering != X_DBL_BUF_NONE)
911 	    XDrawArc(_plotter->x_dpy, _plotter->x_drawable3,
912 		     _plotter->drawstate->x_gc_fg,
913 		     xorigin, yorigin, squaresize_x, squaresize_y,
914 		     startangle, anglerange);
915 	  else
916 	    {
917 	      if (_plotter->x_drawable1)
918 		XDrawArc(_plotter->x_dpy, _plotter->x_drawable1,
919 			 _plotter->drawstate->x_gc_fg,
920 			 xorigin, yorigin, squaresize_x, squaresize_y,
921 			 startangle, anglerange);
922 	      if (_plotter->x_drawable2)
923 		XDrawArc(_plotter->x_dpy, _plotter->x_drawable2,
924 			 _plotter->drawstate->x_gc_fg,
925 			 xorigin, yorigin, squaresize_x, squaresize_y,
926 			 startangle, anglerange);
927 	    }
928 	}
929     }
930 }
931 
932 /**********************************************************************/
933 
934 /* This version of the internal method path_is_flushable() is for XDrawable
935    and X Plotters, which will under some circumstances `pre-draw', i.e.,
936    draw line segments in real time.  (This requires a zero line width and a
937    "solid" line style.).  In that case, this returns false; otherwise it
938    will return true. */
939 
940 bool
_pl_x_path_is_flushable(S___ (Plotter * _plotter))941 _pl_x_path_is_flushable (S___(Plotter *_plotter))
942 {
943   if (_plotter->drawstate->pen_type != 0 /* pen is present */
944       && _plotter->drawstate->line_type == PL_L_SOLID
945       && !_plotter->drawstate->dash_array_in_effect /* really solid */
946       && _plotter->drawstate->points_are_connected /* really, really */
947       && _plotter->drawstate->quantized_device_line_width == 0
948       && !_plotter->drawstate->path->primitive) /* not a builtin */
949     /* we're pre-drawing rather than drawing when endpath() is finally
950        invoked, so flushing out a partially drawn path by invoking
951        endpath() early would be absurd */
952     return false;
953   else
954     /* endpath() will be invoked */
955     return true;
956 }
957 
958 /**********************************************************************/
959 
960 /* This version of the internal method maybe_prepaint_segments() is for
961    XDrawable and X Plotters.  It will draw line segments in real time if
962    the line width is zero, the line style is "solid", and there's no
963    filling to be done.  Also, it requires that the polyline being drawn be
964    unfilled, and not be one one of the polygonalized convex closed
965    primitives (box/circle/ellipse).
966 
967    This hack makes it possible, after doing `graph -TX' (which has a
968    default line width of zero), to type in points manually, and see the
969    corresponding polyline drawn in real time.  The `-x' and `-y' options
970    must of course be specified too, to set the axis limits in advance. */
971 
972 void
_pl_x_maybe_prepaint_segments(R___ (Plotter * _plotter)int prev_num_segments)973 _pl_x_maybe_prepaint_segments (R___(Plotter *_plotter) int prev_num_segments)
974 {
975   int i;
976   bool something_drawn = false;
977 
978   /* sanity check */
979   if (_plotter->drawstate->path->num_segments < 2)
980     return;
981 
982   if (_plotter->drawstate->path->num_segments == prev_num_segments)
983     /* nothing to paint */
984     return;
985 
986   /* Our criteria for pre-drawing line segments: zero-width solid line, and
987      we're not drawing this line segment as part of a polygonalized
988      built-in object (i.e. a rectangles or ellipse).  If the criteria
989      aren't met, we wait until endpath() is invoked, or in general until
990      the path is flushed out, before painting it.
991 
992      If we pre-draw, we don't also draw when endpath() is invoked.  One
993      exception: if the polyline is to be filled.  In that case, at endpath
994      time, we'll fill it and re-edge it. */
995 
996   if (!(_plotter->drawstate->pen_type != 0 /* pen is present */
997 	&& _plotter->drawstate->line_type == PL_L_SOLID
998 	&& !_plotter->drawstate->dash_array_in_effect /* really solid */
999 	&& _plotter->drawstate->points_are_connected /* really, really */
1000 	&& _plotter->drawstate->quantized_device_line_width == 0
1001 	&& !_plotter->drawstate->path->primitive)) /* not a built-in object */
1002     /* we're not pre-drawing */
1003     return;
1004 
1005   /* An X/XDrawable Plotter's segment list, at painting time, will only
1006      contain a polyline (i.e. a sequence of line segments) or a single
1007      circular or elliptic arc.  That's because X/XDrawable Plotters can't
1008      handle `mixed paths'.
1009 
1010      Since they can't handle mixed paths, any single arc that's placed in a
1011      previously empty segment list will need to be replaced by a polygonal
1012      approximation, when and if additional segments need to be added.  The
1013      maybe_replace_arc() function, which is invoked in the base Plotter
1014      code in several places, takes care of that.
1015 
1016      Because of this replacement procedure, maybe_prepaint_segments() may
1017      be invoked on a segment list that consists of a single moveto-arc or
1018      moveto-ellarc pair.  We don't prepaint such things.  Of course if the
1019      arc is subsequently replaced by a polygonal approximation, then we'll
1020      prepaint the polygonal approximation, at that time. */
1021 
1022   if (prev_num_segments == 0 &&
1023       _plotter->drawstate->path->num_segments == 2
1024       && _plotter->drawstate->path->segments[0].type == S_MOVETO
1025       && (_plotter->drawstate->path->segments[1].type == S_ARC
1026 	  || _plotter->drawstate->path->segments[1].type == S_ELLARC))
1027     return;
1028 
1029   if (prev_num_segments == 0)
1030     /* first segment of path; this must be a `moveto' */
1031     {
1032       /* update GC used for drawing */
1033       _pl_x_set_attributes (R___(_plotter) X_GC_FOR_DRAWING);
1034 
1035       /* select pen color as foreground color in GC used for drawing */
1036       _pl_x_set_pen_color (S___(_plotter));
1037     }
1038 
1039   /* Iterate over all segments to be painted.  Because X/XDrawable Plotters
1040      can't handle `mixed paths', this function ends up being called only on
1041      sequences of line segments. */
1042 
1043   for (i = IMAX(1, prev_num_segments);
1044        i < _plotter->drawstate->path->num_segments;
1045        i++)
1046     {
1047       /* use same variables for points #1 and #2, since reusing them works
1048 	 around an obscure bug in gcc 2.7.2.3 that rears its head if -O2 is
1049 	 used */
1050       double xu, yu, xd, yd;
1051       double x, y;
1052       int x1, y1, x2, y2;
1053 
1054       /* starting and ending points for zero-width line segment: (xu,yu)
1055 	 and (x,y) respectively */
1056       xu = _plotter->drawstate->path->segments[i-1].p.x;
1057       yu = _plotter->drawstate->path->segments[i-1].p.y;
1058       x = _plotter->drawstate->path->segments[i].p.x;
1059       y = _plotter->drawstate->path->segments[i].p.y;
1060 
1061       /* convert to integer X11 coordinates */
1062       xd = XD(xu, yu);
1063       yd = YD(xu, yu);
1064       x1 = IROUND(xd);
1065       y1 = IROUND(yd);
1066       xd = XD(x,y);
1067       yd = YD(x,y);
1068       x2 = IROUND(xd);
1069       y2 = IROUND(yd);
1070 
1071       if (x1 != x2 || y1 != y2)
1072 	/* line segment has nonzero length, so draw it */
1073 	{
1074 	  if (_plotter->x_double_buffering != X_DBL_BUF_NONE)
1075 	    /* double buffering, have a `x_drawable3' to draw into */
1076 	    XDrawLine (_plotter->x_dpy, _plotter->x_drawable3,
1077 		       _plotter->drawstate->x_gc_fg, x1, y1, x2, y2);
1078 	  else
1079 	    {
1080 	      if (_plotter->x_drawable1)
1081 		XDrawLine (_plotter->x_dpy, _plotter->x_drawable1,
1082 			   _plotter->drawstate->x_gc_fg, x1, y1, x2, y2);
1083 	      if (_plotter->x_drawable2)
1084 		XDrawLine (_plotter->x_dpy, _plotter->x_drawable2,
1085 			   _plotter->drawstate->x_gc_fg, x1, y1, x2, y2);
1086 	    }
1087 
1088 	  something_drawn = true;
1089 	}
1090       else
1091 	/* line segment in terms of integer device coordinates has zero
1092 	   length; but if it has nonzero length in user coordinates, draw
1093 	   it as a single pixel unless cap type is "butt" */
1094 	if (!(_plotter->drawstate->cap_type == PL_CAP_BUTT
1095 	      && xu == x && yu == y))
1096 	  {
1097 	    if (_plotter->x_double_buffering != X_DBL_BUF_NONE)
1098 	      /* double buffering, have a `x_drawable3' to draw into */
1099 	      XDrawPoint (_plotter->x_dpy, _plotter->x_drawable3,
1100 			  _plotter->drawstate->x_gc_fg,
1101 			  x1, y1);
1102 	    else
1103 	      /* not double buffering */
1104 	      {
1105 		if (_plotter->x_drawable1)
1106 		  XDrawPoint (_plotter->x_dpy, _plotter->x_drawable1,
1107 			      _plotter->drawstate->x_gc_fg,
1108 			      x1, y1);
1109 		if (_plotter->x_drawable2)
1110 		  XDrawPoint (_plotter->x_dpy, _plotter->x_drawable2,
1111 			      _plotter->drawstate->x_gc_fg,
1112 			      x1, y1);
1113 	      }
1114 
1115 	    something_drawn = true;
1116 	  }
1117     }
1118 
1119   if (something_drawn)
1120     /* maybe flush X output buffer and handle X events (a no-op for
1121        XDrawablePlotters, which is overridden for XPlotters) */
1122   _maybe_handle_x_events (S___(_plotter));
1123 }
1124 
1125 bool
_pl_x_paint_paths(S___ (Plotter * _plotter))1126 _pl_x_paint_paths (S___(Plotter *_plotter))
1127 {
1128   return false;
1129 }
1130