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