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