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