xref: /petsc/src/sys/classes/draw/impls/x/xinit.c (revision 09440f2536211689395dd83abbfd05fbf503dd2c)
1 
2 /*
3    This file contains routines to open an X window display and window
4    This consists of a number of routines that set the various
5    fields in the Window structure, which is passed to
6    all of these routines.
7 
8    Note that if you use the default visual and colormap, then you
9    can use these routines with any X toolkit that will give you the
10    Window id of the window that it is managing.  Use that instead of the
11    call to PetscDrawXiCreateWindow .  Similarly for the Display.
12 */
13 
14 #include <../src/sys/classes/draw/impls/x/ximpl.h>
15 
16 extern PetscErrorCode PetscDrawSetColormap_X(PetscDraw_X*,Colormap);
17 
18 /*
19   PetscDrawXiOpenDisplay - Open and setup a display
20 */
21 #undef __FUNCT__
22 #define __FUNCT__ "PetscDrawXiOpenDisplay"
23 static PetscErrorCode PetscDrawXiOpenDisplay(PetscDraw_X *XiWin,const char display[])
24 {
25   PetscFunctionBegin;
26   XiWin->disp = XOpenDisplay(display);
27   if (!XiWin->disp) {
28     SETERRQ1(PETSC_COMM_SELF,PETSC_ERR_LIB,"Unable to open display on %s\n\
29     Make sure your COMPUTE NODES are authorized to connect \n\
30     to this X server and either your DISPLAY variable\n\
31     is set or you use the -display name option\n",display);
32   }
33   XiWin->screen     = DefaultScreen(XiWin->disp);
34   XiWin->vis        = DefaultVisual(XiWin->disp,XiWin->screen);
35   XiWin->depth      = DefaultDepth(XiWin->disp,XiWin->screen);
36   XiWin->cmap       = DefaultColormap(XiWin->disp,XiWin->screen);
37   XiWin->background = WhitePixel(XiWin->disp,XiWin->screen);
38   XiWin->foreground = BlackPixel(XiWin->disp,XiWin->screen);
39   PetscFunctionReturn(0);
40 }
41 
42 #undef __FUNCT__
43 #define __FUNCT__ "PetscDrawXiClose"
44 PetscErrorCode PetscDrawXiClose(PetscDraw_X *XiWin)
45 {
46   PetscErrorCode ierr;
47 
48   PetscFunctionBegin;
49   if (!XiWin) PetscFunctionReturn(0);
50   ierr = PetscFree(XiWin->font);CHKERRQ(ierr);
51   if (XiWin->disp) {
52 #if defined(PETSC_HAVE_SETJMP_H)
53     jmp_buf              jmpbuf;
54     PetscXIOErrorHandler xioerrhdl;
55     ierr = PetscMemcpy(&jmpbuf,&PetscXIOErrorHandlerJumpBuf,sizeof(jmpbuf));CHKERRQ(ierr);
56     xioerrhdl = PetscSetXIOErrorHandler(PetscXIOErrorHandlerJump);
57     if (!setjmp(PetscXIOErrorHandlerJumpBuf))
58 #endif
59     {
60       XFreeGC(XiWin->disp,XiWin->gc.set);
61       XCloseDisplay(XiWin->disp);
62     }
63     XiWin->disp = NULL;
64 #if defined(PETSC_HAVE_SETJMP_H)
65     (void)PetscSetXIOErrorHandler(xioerrhdl);
66     ierr = PetscMemcpy(&PetscXIOErrorHandlerJumpBuf,&jmpbuf,sizeof(jmpbuf));CHKERRQ(ierr);
67 #endif
68   }
69   PetscFunctionReturn(0);
70 }
71 
72 /*
73    PetscDrawXiCreateGC - setup the GC structure
74 */
75 #undef __FUNCT__
76 #define __FUNCT__ "PetscDrawXiCreateGC"
77 static PetscErrorCode PetscDrawXiCreateGC(PetscDraw_X *XiWin,PetscDrawXiPixVal fg)
78 {
79   XGCValues gcvalues;             /* window graphics context values */
80 
81   PetscFunctionBegin;
82   /* Set the graphics contexts */
83   /* create a gc for the ROP_SET operation (writing the fg value to a pixel) */
84   /* (do this with function GXcopy; GXset will automatically write 1) */
85   gcvalues.function   = GXcopy;
86   gcvalues.foreground = fg;
87   XiWin->gc.cur_pix   = fg;
88   XiWin->gc.set       = XCreateGC(XiWin->disp,RootWindow(XiWin->disp,XiWin->screen),GCFunction|GCForeground,&gcvalues);
89   if (!XiWin->gc.set) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"Unable to create X graphics context");
90   PetscFunctionReturn(0);
91 }
92 
93 /*
94    PetscDrawXiInit - basic setup the draw (display, graphics context, font)
95 */
96 #undef __FUNCT__
97 #define __FUNCT__ "PetscDrawXiInit"
98 PetscErrorCode PetscDrawXiInit(PetscDraw_X *XiWin,const char display[])
99 {
100   PetscErrorCode ierr;
101   PetscFunctionBegin;
102   ierr = PetscDrawXiOpenDisplay(XiWin,display);CHKERRQ(ierr);
103   ierr = PetscDrawXiCreateGC(XiWin,XiWin->foreground);CHKERRQ(ierr);
104   ierr = PetscDrawXiFontFixed(XiWin,6,10,&XiWin->font);CHKERRQ(ierr);
105   PetscFunctionReturn(0);
106 }
107 
108 /*
109     This routine waits until the window is actually created or destroyed
110     Returns 0 if window is mapped; 1 if window is destroyed.
111  */
112 #undef __FUNCT__
113 #define __FUNCT__ "PetscDrawXiWaitMap"
114 static PetscErrorCode PetscDrawXiWaitMap(PetscDraw_X *XiWin)
115 {
116   XEvent event;
117 
118   PetscFunctionBegin;
119   while (1) {
120     XMaskEvent(XiWin->disp,ExposureMask|StructureNotifyMask,&event);
121     if (event.xany.window != XiWin->win) break;
122     else {
123       switch (event.type) {
124       case ConfigureNotify:
125         /* window has been moved or resized */
126         XiWin->w = event.xconfigure.width  - 2 * event.xconfigure.border_width;
127         XiWin->h = event.xconfigure.height - 2 * event.xconfigure.border_width;
128         break;
129       case DestroyNotify:
130         PetscFunctionReturn(1);
131       case Expose:
132         PetscFunctionReturn(0);
133         /* else ignore event */
134       }
135     }
136   }
137   PetscFunctionReturn(0);
138 }
139 
140 /*
141     Actually display a window at [x,y] with sizes (w,h)
142 */
143 #undef __FUNCT__
144 #define __FUNCT__ "PetscDrawXiDisplayWindow"
145 static PetscErrorCode PetscDrawXiDisplayWindow(PetscDraw_X *XiWin,char *label,int x,int y,int w,int h)
146 {
147   unsigned int         wavail,havail;
148   XSizeHints           size_hints;
149   XWindowAttributes    in_window_attributes;
150   XSetWindowAttributes window_attributes;
151   unsigned int         border_width = 0;
152   unsigned long        backgnd_pixel = WhitePixel(XiWin->disp,XiWin->screen);
153   unsigned long        wmask;
154 
155   PetscFunctionBegin;
156   /* get the available widths */
157   wavail = DisplayWidth(XiWin->disp,XiWin->screen);
158   havail = DisplayHeight(XiWin->disp,XiWin->screen);
159   if (w <= 0 || h <= 0) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"X Window display has invalid height or width");
160   if ((unsigned int)w > wavail) w = wavail;
161   if ((unsigned int)h > havail) h = havail;
162 
163   if (x < 0) x = (int)(wavail - (unsigned int)w + (unsigned int)x);
164   if (y < 0) y = (int)(havail - (unsigned int)h + (unsigned int)y);
165   x = ((unsigned int)x + w > wavail) ? (int)(wavail - (unsigned int)w) : x;
166   y = ((unsigned int)y + h > havail) ? (int)(havail - (unsigned int)h) : y;
167 
168   /* We need XCreateWindow since we may need an visual other than the default one */
169   XGetWindowAttributes(XiWin->disp,RootWindow(XiWin->disp,XiWin->screen),&in_window_attributes);
170   window_attributes.background_pixmap = None;
171   window_attributes.background_pixel  = backgnd_pixel;
172   /* No border for now */
173   window_attributes.border_pixmap     = None;
174   /*
175   window_attributes.border_pixel      = border_pixel;
176   */
177   window_attributes.bit_gravity       = in_window_attributes.bit_gravity;
178   window_attributes.win_gravity       = in_window_attributes.win_gravity;
179   /* Backing store is too slow in color systems */
180   window_attributes.backing_store     = NotUseful;
181   window_attributes.backing_pixel     = backgnd_pixel;
182   window_attributes.save_under        = 1;
183   window_attributes.event_mask        = 0;
184   window_attributes.do_not_propagate_mask = 0;
185   window_attributes.override_redirect = 0;
186   window_attributes.colormap          = XiWin->cmap;
187   /* None for cursor does NOT mean none, it means cursor of Parent */
188   window_attributes.cursor            = None;
189 
190   wmask = CWBackPixmap | CWBackPixel    | CWBorderPixmap  | CWBitGravity |
191           CWWinGravity | CWBackingStore | CWBackingPixel  | CWOverrideRedirect |
192           CWSaveUnder  | CWEventMask    | CWDontPropagate |
193           CWCursor     | CWColormap;
194 
195   XiWin->win = XCreateWindow(XiWin->disp,RootWindow(XiWin->disp,XiWin->screen),x,y,w,h,border_width,XiWin->depth,InputOutput,XiWin->vis,wmask,&window_attributes);
196   if (!XiWin->win) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"Unable to open X window");
197 
198   /* set window manager hints */
199   {
200     XWMHints      wm_hints;
201     XClassHint    class_hints;
202     XTextProperty windowname,iconname;
203 
204     if (label) XStringListToTextProperty(&label,1,&windowname);
205     else       XStringListToTextProperty(&label,0,&windowname);
206     if (label) XStringListToTextProperty(&label,1,&iconname);
207     else       XStringListToTextProperty(&label,0,&iconname);
208 
209     wm_hints.initial_state = NormalState;
210     wm_hints.input         = True;
211     wm_hints.flags         = StateHint|InputHint;
212 
213     /* These properties can be used by window managers to decide how to display a window */
214     class_hints.res_name  = (char*)"petsc";
215     class_hints.res_class = (char*)"PETSc";
216 
217     size_hints.x          = x;
218     size_hints.y          = y;
219     size_hints.min_width  = 4*border_width;
220     size_hints.min_height = 4*border_width;
221     size_hints.width      = w;
222     size_hints.height     = h;
223     size_hints.flags      = USPosition | USSize | PMinSize;
224 
225     XSetWMProperties(XiWin->disp,XiWin->win,&windowname,&iconname,0,0,&size_hints,&wm_hints,&class_hints);
226     XFree((void*)windowname.value);
227     XFree((void*)iconname.value);
228   }
229 
230   /* make the window visible */
231   XSelectInput(XiWin->disp,XiWin->win,ExposureMask|StructureNotifyMask);
232   XMapWindow(XiWin->disp,XiWin->win);
233 
234   /* some window systems are cruel and interfere with the placement of
235      windows.  We wait here for the window to be created or to die */
236   if (PetscDrawXiWaitMap(XiWin)) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"Wait for X window failed");
237   PetscFunctionReturn(0);
238 }
239 
240 #undef __FUNCT__
241 #define __FUNCT__ "PetscDrawXiQuickWindow"
242 PetscErrorCode PetscDrawXiQuickWindow(PetscDraw_X *XiWin,char *name,int x,int y,int nx,int ny)
243 {
244   Window         root;
245   unsigned int   w,h,dummy;
246   PetscErrorCode ierr;
247 
248   PetscFunctionBegin;
249   ierr = PetscDrawSetColormap_X(XiWin,(Colormap)0);CHKERRQ(ierr);
250   ierr = PetscDrawXiDisplayWindow(XiWin,name,x,y,nx,ny);CHKERRQ(ierr);
251   XSetWindowBackground(XiWin->disp,XiWin->win,XiWin->background);
252   XClearWindow(XiWin->disp,XiWin->win);
253 
254   XGetGeometry(XiWin->disp,XiWin->win,&root,&x,&y,&w,&h,&dummy,&dummy);
255   XiWin->x = x;
256   XiWin->y = y;
257   XiWin->w = (int)w;
258   XiWin->h = (int)h;
259   PetscFunctionReturn(0);
260 }
261 
262 /*
263    A version from an already defined window
264 */
265 #undef __FUNCT__
266 #define __FUNCT__ "PetscDrawXiQuickWindowFromWindow"
267 PetscErrorCode PetscDrawXiQuickWindowFromWindow(PetscDraw_X *XiWin,Window win)
268 {
269   Window            root;
270   int               x,y;
271   unsigned int      w,h,dummy;
272   XWindowAttributes attributes;
273   PetscErrorCode    ierr;
274 
275   PetscFunctionBegin;
276   XiWin->win = win;
277   XGetWindowAttributes(XiWin->disp,XiWin->win,&attributes);
278   ierr = PetscDrawSetColormap_X(XiWin,attributes.colormap);CHKERRQ(ierr);
279 
280   XGetGeometry(XiWin->disp,XiWin->win,&root,&x,&y,&w,&h,&dummy,&dummy);
281   XiWin->x = x;
282   XiWin->y = y;
283   XiWin->w = (int)w;
284   XiWin->h = (int)h;
285   PetscFunctionReturn(0);
286 }
287 
288 
289 #undef __FUNCT__
290 #define __FUNCT__ "PetscDrawXiQuickPixmap"
291 PetscErrorCode PetscDrawXiQuickPixmap(PetscDraw_X* XiWin)
292 {
293   PetscFunctionBegin;
294   if (XiWin->drw) XFreePixmap(XiWin->disp,XiWin->drw);
295   XiWin->drw = XCreatePixmap(XiWin->disp,RootWindow(XiWin->disp,XiWin->screen),XiWin->w,XiWin->h,XiWin->depth);
296   PetscDrawXiSetPixVal(XiWin,XiWin->background);
297   XFillRectangle(XiWin->disp,XiWin->drw,XiWin->gc.set,0,0,XiWin->w,XiWin->h);
298   XSync(XiWin->disp,False);
299   PetscFunctionReturn(0);
300 }
301 
302 PETSC_INTERN PetscErrorCode PetscDrawSetSave_X(PetscDraw,const char[]);
303 
304 #undef __FUNCT__
305 #define __FUNCT__ "PetscDrawSetSave_X"
306 PetscErrorCode PetscDrawSetSave_X(PetscDraw draw,const char *filename)
307 {
308   PetscErrorCode ierr;
309 #if defined(PETSC_HAVE_POPEN)
310   PetscMPIInt    rank;
311 #endif
312 
313   PetscFunctionBegin;
314   PetscValidHeaderSpecific(draw,PETSC_DRAW_CLASSID,1);
315 #if defined(PETSC_HAVE_POPEN)
316   ierr = MPI_Comm_rank(PetscObjectComm((PetscObject)draw),&rank);CHKERRQ(ierr);
317   if (!rank) {
318     char  command[PETSC_MAX_PATH_LEN];
319     FILE  *fd;
320     int   err;
321 
322     ierr = PetscMemzero(command,sizeof(command));CHKERRQ(ierr);
323     ierr = PetscSNPrintf(command,PETSC_MAX_PATH_LEN,"rm -fr %s %s.m4v",draw->savefilename,draw->savefilename);CHKERRQ(ierr);
324     ierr = PetscPOpen(PETSC_COMM_SELF,NULL,command,"r",&fd);CHKERRQ(ierr);
325     ierr = PetscPClose(PETSC_COMM_SELF,fd,&err);CHKERRQ(ierr);
326     ierr = PetscSNPrintf(command,PETSC_MAX_PATH_LEN,"mkdir %s",draw->savefilename);CHKERRQ(ierr);
327     ierr = PetscPOpen(PETSC_COMM_SELF,NULL,command,"r",&fd);CHKERRQ(ierr);
328     ierr = PetscPClose(PETSC_COMM_SELF,fd,&err);CHKERRQ(ierr);
329   }
330 #endif
331   PetscFunctionReturn(0);
332 }
333 
334 
335 #if defined(PETSC_HAVE_AFTERIMAGE)
336 #include <afterimage.h>
337 
338 /* String names of possible Afterimage formats */
339 const char *PetscAfterImageFormats[] = {
340         ".Xpm",
341 	".Xpm.Z",
342 	".Xpm.gz",
343 	".Png",
344 	".Jpeg",
345 	".Xcf", /* Gimp format */
346 	".Ppm",
347 	".Pnm",
348 	"MS Windows Bitmap",
349 	"MS Windows Icon",
350 	"MS Windows Cursor",
351 	".Gif",
352 	".Tiff",
353 	"Afterstep XMLScript",
354 	"Scalable Vector Graphics (SVG)",
355 	".Xbm",
356 	"Targa",
357 	".Pcx",
358 	".HTML",
359 	"XML",
360 	"Unknown"
361 };
362 
363 #undef __FUNCT__
364 #define __FUNCT__ "PetscAfterimageStringToFormat"
365 static PetscErrorCode PetscAfterimageStringToFormat(const char *ext,ASImageFileTypes *format)
366 {
367   PetscInt       i;
368   PetscErrorCode ierr;
369   PetscBool      flg;
370 
371   PetscFunctionBegin;
372   ierr = PetscStrcasecmp(".Jpg",ext,&flg);CHKERRQ(ierr);
373   if (flg) ext = ".Jpeg";
374   for (i=0; i<sizeof(PetscAfterImageFormats)/sizeof(char**); i++) {
375     ierr = PetscStrcasecmp(PetscAfterImageFormats[i],ext,&flg);CHKERRQ(ierr);
376     if (flg) {
377       *format = (ASImageFileTypes)i;
378       PetscFunctionReturn(0);
379     }
380   }
381   *format = ASIT_Unknown;
382   PetscFunctionReturn(0);
383 }
384 
385 #if defined(PETSC_HAVE_SAWS)
386 #include <petscviewersaws.h>
387 /*
388   The PetscAfterimage object and functions are used to maintain a list of file images created by Afterimage that can
389   be displayed by the SAWs webserver.
390 */
391 typedef struct _P_PetscAfterimage *PetscAfterimage;
392 struct _P_PetscAfterimage {
393   PetscAfterimage next;
394   char            *filename;
395   char            *ext;
396   PetscInt        cnt;
397 } ;
398 
399 static PetscAfterimage afterimages = 0;
400 
401 #undef __FUNCT__
402 #define __FUNCT__ "PetscAfterimageDestroy"
403 static PetscErrorCode PetscAfterimageDestroy(void)
404 {
405   PetscErrorCode ierr;
406   PetscAfterimage       afterimage,oafterimage = afterimages;
407 
408   PetscFunctionBegin;
409   while (oafterimage) {
410     afterimage = oafterimage->next;
411     ierr = PetscFree(oafterimage->filename);CHKERRQ(ierr);
412     ierr = PetscFree(oafterimage->ext);CHKERRQ(ierr);
413     ierr = PetscFree(oafterimage);CHKERRQ(ierr);
414     oafterimage = afterimage;
415   }
416   PetscFunctionReturn(0);
417 }
418 
419 #undef __FUNCT__
420 #define __FUNCT__ "PetscAfterimageAdd"
421 static PetscErrorCode PetscAfterimageAdd(const char *filename,const char *ext,PetscInt cnt)
422 {
423   PetscErrorCode   ierr;
424   PetscAfterimage  afterimage,oafterimage = afterimages;
425   PetscBool        flg;
426 
427   PetscFunctionBegin;
428   if (oafterimage){
429     ierr = PetscStrcmp(filename,oafterimage->filename,&flg);CHKERRQ(ierr);
430     if (flg) {
431       oafterimage->cnt = cnt;
432       PetscFunctionReturn(0);
433     }
434     while (oafterimage->next) {
435       oafterimage = oafterimage->next;
436       ierr = PetscStrcmp(filename,oafterimage->filename,&flg);CHKERRQ(ierr);
437       if (flg) {
438         oafterimage->cnt = cnt;
439         PetscFunctionReturn(0);
440       }
441     }
442     ierr = PetscNew(&afterimage);CHKERRQ(ierr);
443     oafterimage->next = afterimage;
444   } else {
445     ierr = PetscNew(&afterimage);CHKERRQ(ierr);
446     afterimages = afterimage;
447   }
448   ierr = PetscStrallocpy(filename,&afterimage->filename);CHKERRQ(ierr);
449   ierr = PetscStrallocpy(ext,&afterimage->ext);CHKERRQ(ierr);
450   afterimage->cnt = cnt;
451   ierr = PetscRegisterFinalize(PetscAfterimageDestroy);CHKERRQ(ierr);
452   PetscFunctionReturn(0);
453 }
454 
455 #endif
456 
457 PETSC_INTERN PetscErrorCode PetscDrawSave_X(PetscDraw);
458 
459 #undef __FUNCT__
460 #define __FUNCT__ "PetscDrawSave_X"
461 PetscErrorCode PetscDrawSave_X(PetscDraw draw)
462 {
463   PetscDraw_X      *drawx = (PetscDraw_X*)draw->data;
464   XImage           *image;
465   ASImage          *asimage;
466   struct  ASVisual *asv;
467   char             filename[PETSC_MAX_PATH_LEN];
468   PetscErrorCode   ierr;
469   PetscMPIInt      rank;
470   int              depth;
471   ASImageFileTypes format;
472 
473   PetscFunctionBegin;
474   if (!draw->savefilename) PetscFunctionReturn(0);
475   ierr = MPI_Comm_rank(PetscObjectComm((PetscObject)draw),&rank);CHKERRQ(ierr);
476 
477   ierr = PetscDrawCollectiveBegin(draw);CHKERRQ(ierr);
478   XSync(drawx->disp,True);
479   ierr = PetscDrawCollectiveEnd(draw);CHKERRQ(ierr);
480   ierr = MPI_Barrier(PetscObjectComm((PetscObject)draw));CHKERRQ(ierr);
481 
482   ierr = PetscDrawCollectiveBegin(draw);CHKERRQ(ierr);
483   if (rank) goto finally; /* only process 0 handles the saving business */
484   depth = DefaultDepth(drawx->disp,drawx->screen);
485   asv   = create_asvisual(drawx->disp,drawx->screen,depth,NULL);if (!asv) SETERRQ(PetscObjectComm((PetscObject)draw),PETSC_ERR_PLIB,"Cannot create AfterImage ASVisual");
486   image = XGetImage(drawx->disp,PetscDrawXiDrawable(drawx),0,0,drawx->w,drawx->h,AllPlanes,ZPixmap);
487   if (!image) SETERRQ(PetscObjectComm((PetscObject)draw),PETSC_ERR_PLIB,"Cannot XGetImage()");
488   asimage = picture_ximage2asimage(asv,image,0,0);if (!asimage) SETERRQ(PetscObjectComm((PetscObject)draw),PETSC_ERR_PLIB,"Cannot create AfterImage ASImage");
489   if (draw->savesinglefile) {
490     ierr = PetscSNPrintf(filename,PETSC_MAX_PATH_LEN,"%s/%s%s",draw->savefilename,draw->savefilename,draw->savefilenameext);CHKERRQ(ierr);
491   } else {
492     ierr = PetscSNPrintf(filename,PETSC_MAX_PATH_LEN,"%s/%s_%d%s",draw->savefilename,draw->savefilename,draw->savefilecount++,draw->savefilenameext);CHKERRQ(ierr);
493   }
494   ierr = PetscAfterimageStringToFormat(draw->savefilenameext,&format);CHKERRQ(ierr);
495   ASImage2file(asimage,0,filename,format,0);
496 #if defined(PETSC_HAVE_SAWS)
497   {
498     char     body[4096];
499     PetscAfterimage afterimage;
500     size_t   len = 0;
501 
502     ierr = PetscAfterimageAdd(draw->savefilename,draw->savefilenameext,draw->savefilecount-1);CHKERRQ(ierr);
503     afterimage  = afterimages;
504     while (afterimage) {
505       if (draw->savesinglefile) {
506         ierr = PetscSNPrintf(body+len,4086-len,"<img src=\"%s/%s%s\" alt=\"None\">",afterimage->filename,afterimage->filename,afterimage->ext);CHKERRQ(ierr);
507       } else {
508         ierr = PetscSNPrintf(body+len,4086-len,"<img src=\"%s/%s_%d%s\" alt=\"None\">",afterimage->filename,afterimage->filename,afterimage->cnt,afterimage->ext);CHKERRQ(ierr);
509       }
510       ierr = PetscStrlen(body,&len);CHKERRQ(ierr);
511       afterimage  = afterimage->next;
512     }
513     ierr = PetscStrcat(body,"<br>\n");CHKERRQ(ierr);
514     if (draw->savefilecount > 0) PetscStackCallSAWs(SAWs_Pop_Body,("index.html",1));
515     PetscStackCallSAWs(SAWs_Push_Body,("index.html",1,body));
516   }
517 #endif
518   destroy_asvisual(asv,0);
519   XDestroyImage(image);
520 finally:
521   ierr = PetscDrawCollectiveEnd(draw);CHKERRQ(ierr);
522   PetscFunctionReturn(0);
523 }
524 
525 /*
526    There are routines wanted by AfterImage for PNG files
527  */
528 void crc32(void) {;}
529 void inflateReset(void) {;}
530 void deflateReset(void) {;}
531 void deflateInit2(void) {;}
532 void deflateInit2_(void) {;}
533 void deflate(void) {;}
534 void deflateEnd(void) {;}
535 
536 #endif
537 
538 
539 
540