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 version is for XDrawablePlotters, and should be identical to
20    y_erase.c (the version for XPlotters) except that XPlotter-specific
21    lines are commented out.  (Search for #if 0...#endif.)  The function
22    y_erase() is of course renamed x_erase() here. */
23 
24 #include "sys-defines.h"
25 #include "extern.h"
26 
27 /* If we aren't double buffering, this is the number of
28    most-recently-allocated color cells that we _don't_ deallocate when we
29    do an erase().  This is a heuristic.  This quantity must be >= 0. */
30 #define NUM_KEPT_COLORS 256
31 
32 /* If we're doing double buffering, when we do an erase() we of course
33    don't deallocate the color cells that were used in the current frame.
34    We also don't deallocate the cells used in the previous NUM_KEPT_FRAMES
35    frames.  This is a heuristic.  This quantity must be >= 0. */
36 #define NUM_KEPT_FRAMES 16
37 
38 bool
_pl_x_erase_page(S___ (Plotter * _plotter))39 _pl_x_erase_page (S___(Plotter *_plotter))
40 {
41   bool head_found;
42   int window_width, window_height;
43   int i, current_frame_number, current_page_number;
44   plColorRecord *cptr, **link = NULL;
45   plDrawState *stateptr;
46 
47   /* set the foreground color in the GC we use for erasing,
48      to be the background color in the drawing state */
49   _pl_x_set_bg_color (S___(_plotter));
50 
51   /* compute rectangle size; note flipped-y convention */
52   window_width = (_plotter->data->imax - _plotter->data->imin) + 1;
53   window_height = (_plotter->data->jmin - _plotter->data->jmax) + 1;
54 
55   if (_plotter->x_double_buffering != X_DBL_BUF_NONE)
56     {
57       /* Following two sorts of server-supported double buffering
58 	 (X_DBL_BUF_DBE, X_DBL_BUF_MBX) are possible only for X Plotters, not
59 	 X Drawable Plotters.  `By hand' double buffering is possible
60 	 for both. */
61 
62 #if 0
63 #ifdef HAVE_X11_EXTENSIONS_XDBE_H
64 #ifdef HAVE_DBE_SUPPORT
65       if (_plotter->x_double_buffering == X_DBL_BUF_DBE)
66 	/* we're using the X double buffering extension */
67 	{
68 	  XdbeSwapInfo info;
69 
70 	  /* Copy current frame of buffered graphics to window.  Implement
71 	     this by swapping the front and back buffers for widget's
72 	     window.  Former front buffer will become graphics buffer.
73 	     Currently, the buffers are `x_drawable2' (front) and `x_drawable3'
74 	     (back, into which we draw). */
75 	  info.swap_window = _plotter->x_drawable2;
76 	  info.swap_action = XdbeUndefined;
77 	  XdbeSwapBuffers (_plotter->x_dpy, &info, 1);
78 	}
79       else
80 #endif /* HAVE_DBE_SUPPORT */
81 #endif /* HAVE_X11_EXTENSIONS_XDBE_H */
82 
83 #ifdef HAVE_X11_EXTENSIONS_MULTIBUF_H
84 #ifdef HAVE_MBX_SUPPORT
85       if (_plotter->x_double_buffering == X_DBL_BUF_MBX)
86 	/* we're using the X multibuffering extension */
87 	{
88 	  Multibuffer multibuf;
89 
90 	  /* Copy current frame of buffered graphics to window.  Implement
91 	     this by making multibuffer into which we've been drawing the
92 	     current multibuffer. */
93 	  XmbufDisplayBuffers (_plotter->x_dpy, 1, &(_plotter->x_drawable3), 0, 0);
94 
95 	  /* swap the two multibuffers, making the other one the off-screen
96 	     graphics buffer into which we draw (`x_drawable3') */
97 	  multibuf = _plotter->x_drawable3;
98 	  _plotter->x_drawable3 = _plotter->y_drawable4;
99 	  _plotter->y_drawable4 = multibuf;
100 	}
101       else
102 #endif /* HAVE_MBX_SUPPORT */
103 #endif /* HAVE_X11_EXTENSIONS_MULTIBUF_H */
104 #endif /* 0 */
105 
106       /* we must be doing double buffering `by hand', rather than using
107          an X protocol extension */
108       if (_plotter->x_double_buffering == X_DBL_BUF_BY_HAND)
109 	{
110 	  /* copy current frame of buffered graphics to drawable(s) */
111 	  if (_plotter->x_drawable1)
112 	    XCopyArea (_plotter->x_dpy,
113 		       _plotter->x_drawable3, _plotter->x_drawable1,
114 		       _plotter->drawstate->x_gc_bg,
115 		       0, 0,
116 		       (unsigned int)window_width,
117 		       (unsigned int)window_height,
118 		       0, 0);
119 	  if (_plotter->x_drawable2)
120 	    XCopyArea (_plotter->x_dpy,
121 		       _plotter->x_drawable3, _plotter->x_drawable2,
122 		       _plotter->drawstate->x_gc_bg,
123 		       0, 0,
124 		       (unsigned int)window_width,
125 		       (unsigned int)window_height,
126 		       0, 0);
127 	}
128 
129       /* irrespective of which of the three sorts of double buffering is
130 	 being performed, clear the (new) graphics buffer, by filling it
131 	 with background color */
132       XFillRectangle (_plotter->x_dpy, _plotter->x_drawable3,
133 		      _plotter->drawstate->x_gc_bg,
134 		      /* upper left corner */
135 		      0, 0,
136 		      (unsigned int)window_width,
137 		      (unsigned int)window_height);
138     }
139   else
140     /* not double buffering at all */
141     {
142       /* erase drawable(s) by filling with background color */
143       if (_plotter->x_drawable1)
144 	XFillRectangle (_plotter->x_dpy, _plotter->x_drawable1,
145 			_plotter->drawstate->x_gc_bg,
146 			/* upper left corner */
147 			0, 0,
148 			(unsigned int)window_width, (unsigned int)window_height);
149       if (_plotter->x_drawable2)
150 	XFillRectangle (_plotter->x_dpy, _plotter->x_drawable2,
151 			_plotter->drawstate->x_gc_bg,
152 			/* upper left corner */
153 			0, 0,
154 			(unsigned int)window_width, (unsigned int)window_height);
155     }
156 
157 #if 0
158   /* If an X Plotter, update background color of y_canvas widget,
159      irrespective of whether or not we're double buffering.  This fixes
160      things so that if the window is resized to a larger size, the new
161      portions of the window will be filled with the correct color. */
162   {
163     Arg wargs[1];		/* werewolves */
164 
165 #ifdef USE_MOTIF
166     XtSetArg (wargs[0], XmNbackground, _plotter->drawstate->x_gc_bgcolor);
167 #else
168     XtSetArg (wargs[0], XtNbackground, _plotter->drawstate->x_gc_bgcolor);
169 #endif
170     XtSetValues (_plotter->y_toplevel, wargs, (Cardinal)1);
171     XtSetValues (_plotter->y_canvas, wargs, (Cardinal)1);
172   }
173 #endif /* 0 */
174 
175   /* Flush the color cell cache, to the extent we can.  But heuristically,
176      keep in the cache a certain number of cells that aren't strictly
177      needed, but which may be needed in the following frames.  There are
178      two cases.
179 
180      1. If we're not double buffering, preserve some maximum number
181           (NUM_KEPT_COLORS) of the most recently allocated cells.
182           Implementing the cache as a list, though suboptimal from the
183           point of view of speed, makes it easy to implement this heuristic.
184      2. If we're double buffering, preserve all cells that were used
185           in the present frame (which was just transferred to the
186           drawable(s), e.g., to an on-screen window).  This is mandatory.
187           But also use a heuristic: preserve all cells used in the
188 	  preceding NUM_KEPT_FRAMES frames.
189 
190      In both cases, if a cached cell is to be preserved, it must contain a
191      genuine pixel value (the `allocated' flag must be set).
192 
193      We also insist that for a cell to be preserved, it have a `page number
194      stamp' equal to the current page number.  That's because XDrawable
195      Plotters, unlike X Plotters, don't free the color cell cache in
196      end_page(), i.e., when closepl() is called.  That's because X Drawable
197      Plotters are `persistent' in the sense the graphics remain visible
198      until the next reopening, and beyond.  So the cache may include cells
199      left over from previous pages, which get freed only here, when erase()
200      is called. */
201 
202   cptr = _plotter->x_colorlist;
203   _plotter->x_colorlist = NULL;
204   i = 0;
205   head_found = false;
206   current_frame_number = _plotter->data->frame_number;
207   current_page_number = _plotter->data->page_number;
208   while (cptr)
209     {
210       plColorRecord *cptrnext;
211 
212       cptrnext = cptr->next;
213       if (cptr->allocated)
214 	{
215 	  if ((_plotter->x_double_buffering == X_DBL_BUF_NONE
216 	       && cptr->page_number == current_page_number
217 	       && i < NUM_KEPT_COLORS)
218 	      ||
219 	      (_plotter->x_double_buffering != X_DBL_BUF_NONE
220 	       && cptr->page_number == current_page_number
221 	       && cptr->frame_number >= current_frame_number - NUM_KEPT_FRAMES))
222 	    /* cached cell contains a genuine pixel value, and it meets our
223 	       criteria, so preserve it */
224 	    {
225 	      if (head_found)
226 		*link = cptr;
227 	      else
228 		{
229 		  _plotter->x_colorlist = cptr;
230 		  head_found = true;
231 		}
232 
233 	      cptr->next = NULL;
234 	      link = &(cptr->next);
235 	      i++;
236 	    }
237 	  else
238 	    /* cached cell contains a genuine pixel value, but it doesn't
239 	       meet our criteria, so deallocate it */
240 	    {
241 	      XFreeColors (_plotter->x_dpy, _plotter->x_cmap,
242 			   &(cptr->rgb.pixel), 1, (unsigned long)0);
243 	      free (cptr);
244 	    }
245 	}
246       else
247 	/* cached cell doesn't include a genuine pixel value, so free it */
248 	free (cptr);
249 
250       cptr = cptrnext;
251     }
252 
253   /* flag status of all colors in GC's in the drawing state stack as false
254      (on account of flushing, may need to be searched for or reallocated) */
255   for (stateptr = _plotter->drawstate; stateptr; stateptr = stateptr->previous)
256     {
257       stateptr->x_gc_fgcolor_status = false;
258       stateptr->x_gc_fillcolor_status = false;
259       stateptr->x_gc_bgcolor_status = false;
260     }
261 
262   /* maybe flush X output buffer and handle X events (a no-op for
263      XDrawablePlotters, which is overridden for XPlotters) */
264   _maybe_handle_x_events (S___(_plotter));
265 
266   return true;
267 }
268