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 #ifdef HAVE_WAITPID
20 #ifdef HAVE_SYS_WAIT_H
21 #define _POSIX_SOURCE		/* for waitpid() */
22 #endif
23 #endif
24 
25 #include "sys-defines.h"
26 #include "extern.h"
27 
28 #ifdef HAVE_UNISTD_H
29 #ifdef HAVE_SYS_TYPES_H
30 #include <sys/types.h>		/* always include before unistd.h */
31 #endif
32 #include <unistd.h>		/* for fork() */
33 #endif
34 
35 /* song and dance to declare waitpid() and define WNOHANG */
36 #ifdef HAVE_WAITPID
37 #ifdef HAVE_SYS_WAIT_H
38 #ifdef HAVE_SYS_TYPES_H
39 #include <sys/types.h>
40 #endif /* HAVE_SYS_TYPES_H */
41 #include <sys/wait.h>		/* for waitpid() */
42 #endif /* HAVE_SYS_WAIT_H */
43 #endif /* HAVE_WAITPID */
44 
45 bool
_pl_y_end_page(S___ (Plotter * _plotter))46 _pl_y_end_page (S___(Plotter *_plotter))
47 {
48   plColorRecord *cptr;
49   plXFontRecord *fptr;
50   Pixmap bg_pixmap = (Pixmap)0;
51   int window_width, window_height;
52   pid_t forkval;
53 
54   /* compute rectangle size; note flipped-y convention */
55   window_width = (_plotter->data->imax - _plotter->data->imin) + 1;
56   window_height = (_plotter->data->jmin - _plotter->data->jmax) + 1;
57 
58   /* if either sort of server-supported double buffering is being used,
59      create background pixmap for Label widget (it doesn't yet have one) */
60   if (_plotter->x_double_buffering == X_DBL_BUF_MBX
61       || _plotter->x_double_buffering == X_DBL_BUF_DBE)
62     {
63       int screen;		/* screen number */
64       Screen *screen_struct;	/* screen structure */
65 
66       screen = DefaultScreen (_plotter->x_dpy);
67       screen_struct = ScreenOfDisplay (_plotter->x_dpy, screen);
68       bg_pixmap = XCreatePixmap(_plotter->x_dpy,
69 				_plotter->x_drawable2,
70 				(unsigned int)window_width,
71 				(unsigned int)window_height,
72 				(unsigned int)PlanesOfScreen(screen_struct));
73 
74       /* copy from off-screen graphics buffer to pixmap */
75       XCopyArea (_plotter->x_dpy, _plotter->x_drawable3, bg_pixmap,
76 		 _plotter->drawstate->x_gc_bg,
77 		 0, 0,
78 		 (unsigned int)window_width, (unsigned int)window_height,
79 		 0, 0);
80 
81       /* pixmap is installed below as background pixmap for Label widget */
82     }
83 
84   /* If double buffering, must make final frame of graphics visible, by
85      copying it from our off-screen graphics buffer `x_drawable3' to window.
86      There are several types of double buffering: the two server-supported
87      types, and the `by hand' type. */
88 
89 #ifdef HAVE_X11_EXTENSIONS_XDBE_H
90 #ifdef HAVE_DBE_SUPPORT
91   if (_plotter->x_double_buffering == X_DBL_BUF_DBE)
92     /* we're using the X double buffering extension; off-screen graphics
93        buffer `x_drawable3' is a back buffer */
94     {
95       XdbeSwapInfo info;
96 
97       /* make final frame of graphics visible by interchanging front and
98          back buffers one last time */
99       info.swap_window = _plotter->x_drawable2;
100       info.swap_action = XdbeUndefined;
101       XdbeSwapBuffers (_plotter->x_dpy, &info, 1);
102 
103       /* free the back buffer */
104       XdbeDeallocateBackBufferName (_plotter->x_dpy, _plotter->x_drawable3);
105     }
106 #endif /* HAVE_DBE_SUPPORT */
107 #endif /* HAVE_X11_EXTENSIONS_XDBE_H */
108 
109 #ifdef HAVE_X11_EXTENSIONS_MULTIBUF_H
110 #ifdef HAVE_MBX_SUPPORT
111   if (_plotter->x_double_buffering == X_DBL_BUF_MBX)
112     /* we're using the X multibuffering extension; off-screen graphics
113        buffer `x_drawable3' is a non-displayed multibuffer */
114     {
115       /* make final frame of graphics visible by making the multibuffer
116 	 into which we're currently drawing the on-screen multibuffer */
117       XmbufDisplayBuffers (_plotter->x_dpy, 1, &(_plotter->x_drawable3), 0, 0);
118     }
119 #endif /* HAVE_MBX_SUPPORT */
120 #endif /* HAVE_X11_EXTENSIONS_MULTIBUF_H */
121 
122   /* if either sort of server-supported double buffering is being used,
123      install the above-created pixmap as background pixmap for the Label
124      widget to use, once the window has been spun off */
125   if (_plotter->x_double_buffering == X_DBL_BUF_MBX
126       || _plotter->x_double_buffering == X_DBL_BUF_DBE)
127     {
128       Arg wargs[2];		/* werewolves */
129 
130       /* install pixmap as Label widget's background pixmap */
131 #ifdef USE_MOTIF
132       XtSetArg (wargs[0], XmNlabelPixmap, (Pixmap)bg_pixmap);
133       XtSetArg (wargs[1], XmNlabelType, XmPIXMAP);
134       XtSetValues (_plotter->y_canvas, wargs, (Cardinal)2);
135 #else
136       XtSetArg (wargs[0], XtNbitmap, (Pixmap)bg_pixmap);
137       XtSetValues (_plotter->y_canvas, wargs, (Cardinal)1);
138 #endif
139     }
140 
141   if (_plotter->x_double_buffering == X_DBL_BUF_BY_HAND)
142     /* we're double buffering _manually_, rather than using either X11
143        protocol extension, so our off-screen graphics buffer `x_drawable3' is
144        an ordinary pixmap */
145 	{
146 	  /* make final frame of graphics visible by copying from pixmap to
147              window */
148 	  XCopyArea (_plotter->x_dpy, _plotter->x_drawable3, _plotter->x_drawable2,
149 		     _plotter->drawstate->x_gc_bg,
150 		     0, 0,
151 		     (unsigned int)window_width, (unsigned int)window_height,
152 		     0, 0);
153 	}
154 
155   /* Finally: if we're not double buffering at all, we copy our off-screen
156      graphics buffer to the window.  The off-screen graphics buffer is just
157      the Label widget's background pixmap, `x_drawable1'. */
158   if (_plotter->x_double_buffering == X_DBL_BUF_NONE)
159     XCopyArea (_plotter->x_dpy, _plotter->x_drawable1, _plotter->x_drawable2,
160 	       _plotter->drawstate->x_gc_bg,
161 	       0, 0,
162 	       (unsigned int)window_width, (unsigned int)window_height,
163 	       0, 0);
164 
165   /* following two deallocations (of font records and color cell records)
166      arrange things so that when drawing the next page of graphics, which
167      will require another connection to the X server, the Plotter will
168      start with a clean slate */
169 
170   /* Free font records from Plotter's cache list.  This involves
171      deallocating the font name and also the XFontStruct contained in each
172      record, if non-NULL.  (NULL indicates that the font could not be
173      retrieved.)  */
174   fptr = _plotter->x_fontlist;
175   _plotter->x_fontlist = NULL;
176   while (fptr)
177     {
178       plXFontRecord *fptrnext;
179 
180       fptrnext = fptr->next;
181       free (fptr->x_font_name);
182       if (fptr->x_font_struct)
183 	XFreeFont (_plotter->x_dpy, fptr->x_font_struct);
184       free (fptr);
185       fptr = fptrnext;
186     }
187 
188   /* Free cached color cells from Plotter's cache list.  Do _not_ ask the
189      server to deallocate the cells themselves, because the child process
190      will need them; just free local storage. */
191   cptr = _plotter->x_colorlist;
192   _plotter->x_colorlist = NULL;
193   while (cptr)
194     {
195       plColorRecord *cptrnext;
196 
197       cptrnext = cptr->next;
198       free (cptr);
199       cptr = cptrnext;
200     }
201 
202   /* A bit of last-minute cleanup (could be done elsewhere): call waitpid()
203      to reclaim resources used by zombie child processes resulting from
204      previous closepl()'s, if any.  If this isn't done, the controlling
205      process of any previously popped-up window won't fully exit (e.g. when
206      `q' is typed in the window): it'll remain in the process table as a
207      zombie until the parent process executes. */
208 #ifdef HAVE_WAITPID
209 #ifdef HAVE_SYS_WAIT_H
210 #ifdef WNOHANG
211   {
212     int i;
213 
214     /* iterate over all previously forked-off children (should really keep
215        track of which have exited, since once a child has exited, invoking
216        waitpid() on it is pointless) */
217     for (i = 0; i < _plotter->y_num_pids; i++)
218       waitpid (_plotter->y_pids[i], (int *)NULL, WNOHANG);
219   }
220 #endif
221 #endif
222 #endif
223 
224   /* maybe flush X output buffer and handle X events (a no-op for
225      XDrawablePlotters, which is overridden for XPlotters) */
226   _maybe_handle_x_events (S___(_plotter));
227 
228   /* flush out the X output buffer; wait till all requests have been
229      received and processed by server (see x_flushpl.c) */
230   _pl_x_flush_output (S___(_plotter));
231 
232   /* flush output streams for all Plotters before forking */
233   _pl_g_flush_plotter_outstreams (S___(_plotter));
234 
235   /* DO IT, MAN! */
236   forkval = fork ();
237   if ((int)forkval > 0		/* fork succeeded, and we're the parent */
238       || (int)forkval < 0)	/* fork failed */
239     {
240       bool retval = true;
241 
242       if ((int)forkval < 0)
243 	_plotter->error (R___(_plotter) "the process could not be forked");
244 
245       /* Close connection to X display associated with window that the
246 	 child process should manage, i.e. with the last openpl() invoked
247 	 on this Plotter. */
248       if (close (ConnectionNumber (_plotter->x_dpy)) < 0
249 	  && errno != EINTR)
250 	/* emphatically shouldn't happen */
251 	{
252 	  _plotter->error (R___(_plotter) "the connection to the X display could not be closed");
253 	  retval = false;
254 	}
255 
256       if ((int)forkval > 0)
257 	/* there's a child process, so save its pid */
258 	{
259 	  if (_plotter->y_num_pids == 0)
260 	    _plotter->y_pids = (pid_t *)_pl_xmalloc (sizeof (pid_t));
261 	  else
262 	    _plotter->y_pids =
263 	      (pid_t *)_pl_xrealloc (_plotter->y_pids,
264 				       ((_plotter->y_num_pids + 1)
265 					* sizeof (pid_t)));
266 	  _plotter->y_pids[_plotter->y_num_pids] = forkval;
267 	  _plotter->y_num_pids++;
268 	}
269 
270       /* do teardown of X-specific elements of the first drawing state on
271 	 the drawing state stack */
272       _pl_x_delete_gcs_from_first_drawing_state (S___(_plotter));
273 
274       return retval;
275     }
276 
277   else		/* forkval = 0; fork succeeded, and we're the child */
278     {
279       bool need_redisplay = false;
280       int i;
281 
282       /* Alter canvas widget's translation table, so that exit will occur
283 	 when `q' is typed (or mouse is clicked).  See y_openpl.c. */
284       _pl_y_set_data_for_quitting (S___(_plotter));
285 
286       /* Close all connections to X display other than our own, i.e., close
287 	 all connections that other XPlotters may have been using.  No need
288 	 to lock the global variables _xplotters and _xplotters_len; since
289 	 we've forked and we're the child process, we're the only thread
290 	 left. :-)
291 
292 	 We'll never be accessing those variables again (the only way we
293 	 could would be if we were to call _maybe_handle_x_events(), and
294 	 we aren't going to do that).  So we don't need to worry that they
295 	 may actually be locked.  I.e. there was no need for us to register
296 	 a handler to unlock them immediately after forking, by invoking
297 	 pthread_atfork().  Which is why we didn't do that. */
298 
299       for (i = 0; i < _xplotters_len; i++)
300 	if (_xplotters[i] != NULL
301 	    && _xplotters[i] != _plotter
302 	    && _xplotters[i]->data->opened
303 	    && _xplotters[i]->data->open
304 	    && close (ConnectionNumber (_xplotters[i]->x_dpy)) < 0
305 	    && errno != EINTR)
306 	  /* shouldn't happen */
307 	  _plotter->error (R___(_plotter)
308 			   "the connection to the X display could not be closed");
309 
310       /* Repaint by sending an expose event to ourselves, copying the Label
311 	 widget's background pixmap into its window.  This is a good idea
312 	 because the window could have been resized during the
313 	 openpl..closepl.  We don't do this if not double buffering (and
314 	 presumably animating), unless the window size has changed since
315 	 openpl was invoked (repainting makes the window flash, possibly
316 	 irritating users). */
317       if (_plotter->x_double_buffering != X_DBL_BUF_NONE)
318 	need_redisplay = true;
319       else
320 	{
321 	  Arg wargs[2];		/* werewolves */
322 	  Dimension our_window_height, our_window_width;
323 
324 #ifdef USE_MOTIF
325 	  XtSetArg (wargs[0], XmNwidth, &our_window_width);
326 	  XtSetArg (wargs[1], XmNheight, &our_window_height);
327 #else
328 	  XtSetArg (wargs[0], XtNwidth, &our_window_width);
329 	  XtSetArg (wargs[1], XtNheight, &our_window_height);
330 #endif
331 	  XtGetValues (_plotter->y_canvas, wargs, (Cardinal)2);
332 	  if ((_plotter->data->imax + 1 != (int)our_window_width)
333 	      || (_plotter->data->jmin + 1 != (int)our_window_height))
334 	    /* window changed size */
335 	    need_redisplay = true;
336 	}
337 
338       /* turn off backing store (if used); when we send the expose event to
339 	 ourselves we want to repaint from the background pixmap, NOT from
340 	 the server's backing store */
341       {
342 	XSetWindowAttributes attributes;
343 	unsigned long value_mask;
344 
345 	attributes.backing_store = NotUseful;
346 	value_mask = CWBackingStore;
347 	XChangeWindowAttributes (_plotter->x_dpy, (Window)_plotter->x_drawable2,
348 				 value_mask, &attributes);
349       }
350 
351       if (need_redisplay)
352 	/* send expose event to ourselves */
353 	XClearArea (_plotter->x_dpy,
354 		    (Window)_plotter->x_drawable2,
355 		    0, 0,
356 		    (unsigned int)0, (unsigned int)0,
357 		    True);
358 
359       _plotter->data->open = false; /* flag Plotter as closed (is this useful,
360 				       or just pedantic?) */
361 
362       /* Manage the window.  We won't get any events associated with other
363 	 windows i.e. with previous invocations of openpl..closepl on this
364 	 Plotter, or with other Plotters, since there's a distinct
365 	 application context for every openpl..closepl. */
366       XtAppMainLoop (_plotter->y_app_con); /* shouldn't return */
367 
368       /* NOTREACHED */
369       exit (EXIT_FAILURE);
370     }
371 }
372