]> granicus.if.org Git - imagemagick/blob - MagickCore/animate.c
(no commit message)
[imagemagick] / MagickCore / animate.c
1 /*
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 %                                                                             %
4 %                                                                             %
5 %                                                                             %
6 %                AAA   N   N  IIIII  M   M   AAA   TTTTT  EEEEE               %
7 %               A   A  NN  N    I    MM MM  A   A    T    E                   %
8 %               AAAAA  N N N    I    M M M  AAAAA    T    EEE                 %
9 %               A   A  N  NN    I    M   M  A   A    T    E                   %
10 %               A   A  N   N  IIIII  M   M  A   A    T    EEEEE               %
11 %                                                                             %
12 %                                                                             %
13 %              Methods to Interactively Animate an Image Sequence             %
14 %                                                                             %
15 %                             Software Design                                 %
16 %                               John Cristy                                   %
17 %                                July 1992                                    %
18 %                                                                             %
19 %                                                                             %
20 %  Copyright 1999-2012 ImageMagick Studio LLC, a non-profit organization      %
21 %  dedicated to making software imaging solutions freely available.           %
22 %                                                                             %
23 %  You may not use this file except in compliance with the License.  You may  %
24 %  obtain a copy of the License at                                            %
25 %                                                                             %
26 %    http://www.imagemagick.org/script/license.php                            %
27 %                                                                             %
28 %  Unless required by applicable law or agreed to in writing, software        %
29 %  distributed under the License is distributed on an "AS IS" BASIS,          %
30 %  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
31 %  See the License for the specific language governing permissions and        %
32 %  limitations under the License.                                             %
33 %                                                                             %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35 %
36 %
37 */
38 \f
39 /*
40   Include declarations.
41 */
42 #include "MagickCore/studio.h"
43 #include "MagickCore/animate.h"
44 #include "MagickCore/animate-private.h"
45 #include "MagickCore/client.h"
46 #include "MagickCore/color.h"
47 #include "MagickCore/color-private.h"
48 #include "MagickCore/colorspace.h"
49 #include "MagickCore/constitute.h"
50 #include "MagickCore/delegate.h"
51 #include "MagickCore/exception.h"
52 #include "MagickCore/exception-private.h"
53 #include "MagickCore/geometry.h"
54 #include "MagickCore/image-private.h"
55 #include "MagickCore/layer.h"
56 #include "MagickCore/list.h"
57 #include "MagickCore/log.h"
58 #include "MagickCore/image.h"
59 #include "MagickCore/memory_.h"
60 #include "MagickCore/monitor.h"
61 #include "MagickCore/monitor-private.h"
62 #include "MagickCore/option.h"
63 #include "MagickCore/pixel-accessor.h"
64 #include "MagickCore/property.h"
65 #include "MagickCore/resource_.h"
66 #include "MagickCore/string_.h"
67 #include "MagickCore/string-private.h"
68 #include "MagickCore/transform.h"
69 #include "MagickCore/utility.h"
70 #include "MagickCore/utility-private.h"
71 #include "MagickCore/version.h"
72 #include "MagickCore/widget.h"
73 #include "MagickCore/widget-private.h"
74 #include "MagickCore/xwindow.h"
75 #include "MagickCore/xwindow-private.h"
76 \f
77 #if defined(MAGICKCORE_X11_DELEGATE)
78 /*
79   Animate state declarations.
80 */
81 #define AutoReverseAnimationState 0x0004
82 #define ForwardAnimationState 0x0008
83 #define HighlightState  0x0010
84 #define PlayAnimationState 0x0020
85 #define RepeatAnimationState 0x0040
86 #define StepAnimationState 0x0080
87
88 /*
89   Static declarations.
90 */
91 static const char
92   *AnimateHelp[]=
93   {
94     "BUTTONS",
95     "",
96     "  Press any button to map or unmap the Command widget.",
97     "",
98     "COMMAND WIDGET",
99     "  The Command widget lists a number of sub-menus and commands.",
100     "  They are",
101     "",
102     "    Animate",
103     "      Open...",
104     "      Save...",
105     "      Play",
106     "      Step",
107     "      Repeat",
108     "      Auto Reverse",
109     "    Speed",
110     "      Slower",
111     "      Faster",
112     "    Direction",
113     "      Forward",
114     "      Reverse",
115     "      Help",
116     "        Overview",
117     "        Browse Documentation",
118     "        About Animate",
119     "    Image Info",
120     "    Quit",
121     "",
122     "  Menu items with a indented triangle have a sub-menu.  They",
123     "  are represented above as the indented items.  To access a",
124     "  sub-menu item, move the pointer to the appropriate menu and",
125     "  press a button and drag.  When you find the desired sub-menu",
126     "  item, release the button and the command is executed.  Move",
127     "  the pointer away from the sub-menu if you decide not to",
128     "  execute a particular command.",
129     "",
130     "KEYBOARD ACCELERATORS",
131     "  Accelerators are one or two key presses that effect a",
132     "  particular command.  The keyboard accelerators that",
133     "  animate(1) understands is:",
134     "",
135     "  Ctl+O  Press to open an image from a file.",
136     "",
137     "  space  Press to display the next image in the sequence.",
138     "",
139     "  <      Press to speed-up the display of the images.  Refer to",
140     "         -delay for more information.",
141     "",
142     "  >      Press to slow the display of the images.  Refer to",
143     "         -delay for more information.",
144     "",
145     "  F1     Press to display helpful information about animate(1).",
146     "",
147     "  Find   Press to browse documentation about ImageMagick.",
148     "",
149     "  ?      Press to display information about the image.  Press",
150     "         any key or button to erase the information.",
151     "",
152     "         This information is printed: image name;  image size;",
153     "         and the total number of unique colors in the image.",
154     "",
155     "  Ctl-q  Press to discard all images and exit program.",
156     (char *) NULL
157   };
158 \f
159 /*
160   Constant declarations.
161 */
162 static const char
163   *PageSizes[]=
164   {
165     "Letter",
166     "Tabloid",
167     "Ledger",
168     "Legal",
169     "Statement",
170     "Executive",
171     "A3",
172     "A4",
173     "A5",
174     "B4",
175     "B5",
176     "Folio",
177     "Quarto",
178     "10x14",
179     (char *) NULL
180   };
181
182 static const unsigned char
183   HighlightBitmap[8] =
184   {
185     (unsigned char) 0xaa,
186     (unsigned char) 0x55,
187     (unsigned char) 0xaa,
188     (unsigned char) 0x55,
189     (unsigned char) 0xaa,
190     (unsigned char) 0x55,
191     (unsigned char) 0xaa,
192     (unsigned char) 0x55
193   },
194   ShadowBitmap[8] =
195   {
196     (unsigned char) 0x00,
197     (unsigned char) 0x00,
198     (unsigned char) 0x00,
199     (unsigned char) 0x00,
200     (unsigned char) 0x00,
201     (unsigned char) 0x00,
202     (unsigned char) 0x00,
203     (unsigned char) 0x00
204   };
205 \f
206 /*
207   Enumeration declarations.
208 */
209 typedef enum
210 {
211   OpenCommand,
212   SaveCommand,
213   PlayCommand,
214   StepCommand,
215   RepeatCommand,
216   AutoReverseCommand,
217   SlowerCommand,
218   FasterCommand,
219   ForwardCommand,
220   ReverseCommand,
221   HelpCommand,
222   BrowseDocumentationCommand,
223   VersionCommand,
224   InfoCommand,
225   QuitCommand,
226   StepBackwardCommand,
227   StepForwardCommand,
228   NullCommand
229 } CommandType;
230 \f
231 /*
232   Stipples.
233 */
234 #define HighlightWidth  8
235 #define HighlightHeight  8
236 #define ShadowWidth  8
237 #define ShadowHeight  8
238 \f
239 /*
240   Forward declarations.
241 */
242 static Image
243   *XMagickCommand(Display *,XResourceInfo *,XWindows *,const CommandType,
244     Image **,MagickStatusType *,ExceptionInfo *);
245
246 static MagickBooleanType
247   XSaveImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *);
248 \f
249 /*
250 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
251 %                                                                             %
252 %                                                                             %
253 %                                                                             %
254 %   A n i m a t e I m a g e s                                                 %
255 %                                                                             %
256 %                                                                             %
257 %                                                                             %
258 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
259 %
260 %  AnimateImages() repeatedly displays an image sequence to any X window
261 %  screen.  It returns a value other than 0 if successful.  Check the
262 %  exception member of image to determine the reason for any failure.
263 %
264 %  The format of the AnimateImages method is:
265 %
266 %      MagickBooleanType AnimateImages(const ImageInfo *image_info,
267 %        Image *images,ExceptionInfo *exception)
268 %
269 %  A description of each parameter follows:
270 %
271 %    o image_info: the image info.
272 %
273 %    o image: the image.
274 %
275 %    o exception: return any errors or warnings in this structure.
276 %
277 */
278 MagickExport MagickBooleanType AnimateImages(const ImageInfo *image_info,
279   Image *images,ExceptionInfo *exception)
280 {
281   char
282     *argv[1];
283
284   Display
285     *display;
286
287   MagickStatusType
288     status;
289
290   XrmDatabase
291     resource_database;
292
293   XResourceInfo
294     resource_info;
295
296   assert(image_info != (const ImageInfo *) NULL);
297   assert(image_info->signature == MagickSignature);
298   assert(images != (Image *) NULL);
299   assert(images->signature == MagickSignature);
300   if (images->debug != MagickFalse)
301     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
302   display=XOpenDisplay(image_info->server_name);
303   if (display == (Display *) NULL)
304     {
305       (void) ThrowMagickException(exception,GetMagickModule(),XServerError,
306         "UnableToOpenXServer","`%s'",XDisplayName(image_info->server_name));
307       return(MagickFalse);
308     }
309   if (exception->severity != UndefinedException)
310     CatchException(exception);
311   (void) XSetErrorHandler(XError);
312   resource_database=XGetResourceDatabase(display,GetClientName());
313   (void) ResetMagickMemory(&resource_info,0,sizeof(XResourceInfo));
314   XGetResourceInfo(image_info,resource_database,GetClientName(),&resource_info);
315   if (image_info->page != (char *) NULL)
316     resource_info.image_geometry=AcquireString(image_info->page);
317   resource_info.immutable=MagickTrue;
318   argv[0]=AcquireString(GetClientName());
319   (void) XAnimateImages(display,&resource_info,argv,1,images,exception);
320   (void) SetErrorHandler((ErrorHandler) NULL);
321   (void) SetWarningHandler((WarningHandler) NULL);
322   argv[0]=DestroyString(argv[0]);
323   (void) XCloseDisplay(display);
324   XDestroyResourceInfo(&resource_info);
325   status=exception->severity == UndefinedException ? MagickTrue : MagickFalse;
326   return(status != 0 ? MagickTrue : MagickFalse);
327 }
328 \f
329 /*
330 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
331 %                                                                             %
332 %                                                                             %
333 %                                                                             %
334 +   X M a g i c k C o m m a n d                                               %
335 %                                                                             %
336 %                                                                             %
337 %                                                                             %
338 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
339 %
340 %  XMagickCommand() makes a transform to the image or Image window as specified
341 %  by a user menu button or keyboard command.
342 %
343 %  The format of the XMagickCommand method is:
344 %
345 %      Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
346 %        XWindows *windows,const CommandType command_type,Image **image,
347 %        MagickStatusType *state,ExceptionInfo *exception)
348 %
349 %  A description of each parameter follows:
350 %
351 %    o display: Specifies a connection to an X server; returned from
352 %      XOpenDisplay.
353 %
354 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
355 %
356 %    o windows: Specifies a pointer to a XWindows structure.
357 %
358 %    o image: the image;  XMagickCommand
359 %      may transform the image and return a new image pointer.
360 %
361 %    o state: Specifies a MagickStatusType;  XMagickCommand may return a
362 %      modified state.
363 %
364 %    o exception: return any errors or warnings in this structure.
365 %
366 %
367 */
368 static Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
369   XWindows *windows,const CommandType command_type,Image **image,
370   MagickStatusType *state,ExceptionInfo *exception)
371 {
372   Image
373     *nexus;
374
375   MagickBooleanType
376     proceed;
377
378   MagickStatusType
379     status;
380
381   XTextProperty
382     window_name;
383
384   /*
385     Process user command.
386   */
387   nexus=NewImageList();
388   switch (command_type)
389   {
390     case OpenCommand:
391     {
392       char
393         **filelist;
394
395       Image
396         *images,
397         *next;
398
399       ImageInfo
400         *read_info;
401
402       int
403         number_files;
404
405       register int
406         i;
407
408       static char
409         filenames[MaxTextExtent] = "*";
410
411       if (resource_info->immutable != MagickFalse)
412         break;
413       /*
414         Request file name from user.
415       */
416       XFileBrowserWidget(display,windows,"Animate",filenames);
417       if (*filenames == '\0')
418         return((Image *) NULL);
419       /*
420         Expand the filenames.
421       */
422       filelist=(char **) AcquireMagickMemory(sizeof(char *));
423       if (filelist == (char **) NULL)
424         {
425           ThrowXWindowException(ResourceLimitError,"MemoryAllocationFailed",
426             filenames);
427           return((Image *) NULL);
428         }
429       number_files=1;
430       filelist[0]=filenames;
431       status=ExpandFilenames(&number_files,&filelist);
432       if ((status == MagickFalse) || (number_files == 0))
433         {
434           if (number_files == 0)
435             {
436               ThrowXWindowException(ImageError,"NoImagesWereLoaded",filenames);
437              return((Image *) NULL);
438             }
439           ThrowXWindowException(ResourceLimitError,"MemoryAllocationFailed",
440             filenames);
441           return((Image *) NULL);
442         }
443       read_info=CloneImageInfo(resource_info->image_info);
444       images=NewImageList();
445       XSetCursorState(display,windows,MagickTrue);
446       XCheckRefreshWindows(display,windows);
447       for (i=0; i < number_files; i++)
448       {
449         (void) CopyMagickString(read_info->filename,filelist[i],MaxTextExtent);
450         filelist[i]=DestroyString(filelist[i]);
451         *read_info->magick='\0';
452         next=ReadImage(read_info,exception);
453         CatchException(exception);
454         if (next != (Image *) NULL)
455           AppendImageToList(&images,next);
456         if (number_files <= 5)
457           continue;
458         proceed=SetImageProgress(images,LoadImageTag,i,(MagickSizeType)
459           number_files);
460         if (proceed == MagickFalse)
461           break;
462       }
463       filelist=(char **) RelinquishMagickMemory(filelist);
464       read_info=DestroyImageInfo(read_info);
465       if (images == (Image *) NULL)
466         {
467           XSetCursorState(display,windows,MagickFalse);
468           ThrowXWindowException(ImageError,"NoImagesWereLoaded",filenames);
469           return((Image *) NULL);
470         }
471       nexus=GetFirstImageInList(images);
472       *state|=ExitState;
473       break;
474     }
475     case PlayCommand:
476     {
477       char
478         basename[MaxTextExtent];
479
480       int
481         status;
482
483       /*
484         Window name is the base of the filename.
485       */
486       *state|=PlayAnimationState;
487       *state&=(~AutoReverseAnimationState);
488       GetPathComponent((*image)->magick_filename,BasePath,basename);
489       (void) FormatLocaleString(windows->image.name,MaxTextExtent,
490         "%s: %s",MagickPackageName,basename);
491       if (resource_info->title != (char *) NULL)
492         {
493           char
494             *title;
495
496           title=InterpretImageProperties(resource_info->image_info,*image,
497             resource_info->title,exception);
498           (void) CopyMagickString(windows->image.name,title,MaxTextExtent);
499           title=DestroyString(title);
500         }
501       status=XStringListToTextProperty(&windows->image.name,1,&window_name);
502       if (status == 0)
503         break;
504       XSetWMName(display,windows->image.id,&window_name);
505       (void) XFree((void *) window_name.value);
506       break;
507     }
508     case StepCommand:
509     case StepBackwardCommand:
510     case StepForwardCommand:
511     {
512       *state|=StepAnimationState;
513       *state&=(~PlayAnimationState);
514       if (command_type == StepBackwardCommand)
515         *state&=(~ForwardAnimationState);
516       if (command_type == StepForwardCommand)
517         *state|=ForwardAnimationState;
518       if (resource_info->title != (char *) NULL)
519         break;
520       break;
521     }
522     case RepeatCommand:
523     {
524       *state|=RepeatAnimationState;
525       *state&=(~AutoReverseAnimationState);
526       *state|=PlayAnimationState;
527       break;
528     }
529     case AutoReverseCommand:
530     {
531       *state|=AutoReverseAnimationState;
532       *state&=(~RepeatAnimationState);
533       *state|=PlayAnimationState;
534       break;
535     }
536     case SaveCommand:
537     {
538       /*
539         Save image.
540       */
541       status=XSaveImage(display,resource_info,windows,*image,exception);
542       if (status == MagickFalse)
543         {
544           char
545             message[MaxTextExtent];
546
547           (void) FormatLocaleString(message,MaxTextExtent,"%s:%s",
548             exception->reason != (char *) NULL ? exception->reason : "",
549             exception->description != (char *) NULL ? exception->description :
550             "");
551           XNoticeWidget(display,windows,"Unable to save file:",message);
552           break;
553         }
554       break;
555     }
556     case SlowerCommand:
557     {
558       resource_info->delay++;
559       break;
560     }
561     case FasterCommand:
562     {
563       if (resource_info->delay == 0)
564         break;
565       resource_info->delay--;
566       break;
567     }
568     case ForwardCommand:
569     {
570       *state=ForwardAnimationState;
571       *state&=(~AutoReverseAnimationState);
572       break;
573     }
574     case ReverseCommand:
575     {
576       *state&=(~ForwardAnimationState);
577       *state&=(~AutoReverseAnimationState);
578       break;
579     }
580     case InfoCommand:
581     {
582       XDisplayImageInfo(display,resource_info,windows,(Image *) NULL,*image,
583         exception);
584       break;
585     }
586     case HelpCommand:
587     {
588       /*
589         User requested help.
590       */
591       XTextViewWidget(display,resource_info,windows,MagickFalse,
592         "Help Viewer - Animate",AnimateHelp);
593       break;
594     }
595     case BrowseDocumentationCommand:
596     {
597       Atom
598         mozilla_atom;
599
600       Window
601         mozilla_window,
602         root_window;
603
604       /*
605         Browse the ImageMagick documentation.
606       */
607       root_window=XRootWindow(display,XDefaultScreen(display));
608       mozilla_atom=XInternAtom(display,"_MOZILLA_VERSION",MagickFalse);
609       mozilla_window=XWindowByProperty(display,root_window,mozilla_atom);
610       if (mozilla_window != (Window) NULL)
611         {
612           char
613             command[MaxTextExtent],
614             *url;
615
616           /*
617             Display documentation using Netscape remote control.
618           */
619           url=GetMagickHomeURL();
620           (void) FormatLocaleString(command,MaxTextExtent,
621             "openurl(%s,new-tab)",url);
622           url=DestroyString(url);
623           mozilla_atom=XInternAtom(display,"_MOZILLA_COMMAND",MagickFalse);
624           (void) XChangeProperty(display,mozilla_window,mozilla_atom,
625             XA_STRING,8,PropModeReplace,(unsigned char *) command,
626             (int) strlen(command));
627           XSetCursorState(display,windows,MagickFalse);
628           break;
629         }
630       XSetCursorState(display,windows,MagickTrue);
631       XCheckRefreshWindows(display,windows);
632       status=InvokeDelegate(resource_info->image_info,*image,"browse",
633         (char *) NULL,exception);
634       if (status == MagickFalse)
635         XNoticeWidget(display,windows,"Unable to browse documentation",
636           (char *) NULL);
637       XDelay(display,1500);
638       XSetCursorState(display,windows,MagickFalse);
639       break;
640     }
641     case VersionCommand:
642     {
643       XNoticeWidget(display,windows,GetMagickVersion((size_t *) NULL),
644         GetMagickCopyright());
645       break;
646     }
647     case QuitCommand:
648     {
649       /*
650         exit program
651       */
652       if (resource_info->confirm_exit == MagickFalse)
653         XClientMessage(display,windows->image.id,windows->im_protocols,
654           windows->im_exit,CurrentTime);
655       else
656         {
657           int
658             status;
659
660           /*
661             Confirm program exit.
662           */
663           status=XConfirmWidget(display,windows,"Do you really want to exit",
664             resource_info->client_name);
665           if (status != 0)
666             XClientMessage(display,windows->image.id,windows->im_protocols,
667               windows->im_exit,CurrentTime);
668         }
669       break;
670     }
671     default:
672       break;
673   }
674   return(nexus);
675 }
676 \f
677 /*
678 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
679 %                                                                             %
680 %                                                                             %
681 %                                                                             %
682 +   X A n i m a t e B a c k g r o u n d I m a g e                             %
683 %                                                                             %
684 %                                                                             %
685 %                                                                             %
686 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
687 %
688 %  XAnimateBackgroundImage() animates an image sequence in the background of
689 %  a window.
690 %
691 %  The format of the XAnimateBackgroundImage method is:
692 %
693 %      void XAnimateBackgroundImage(Display *display,
694 %        XResourceInfo *resource_info,Image *images,ExceptionInfo *exception)
695 %
696 %  A description of each parameter follows:
697 %
698 %    o display: Specifies a connection to an X server;  returned from
699 %      XOpenDisplay.
700 %
701 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
702 %
703 %    o images: the image list.
704 %
705 %    o exception: return any errors or warnings in this structure.
706 %
707 */
708
709 static inline ssize_t MagickMax(const ssize_t x,const ssize_t y)
710 {
711   if (x > y)
712     return(x);
713   return(y);
714 }
715
716 #if defined(__cplusplus) || defined(c_plusplus)
717 extern "C" {
718 #endif
719
720 static int SceneCompare(const void *x,const void *y)
721 {
722   const Image
723     **image_1,
724     **image_2;
725
726   image_1=(const Image **) x;
727   image_2=(const Image **) y;
728   return((int) ((*image_1)->scene-(*image_2)->scene));
729 }
730
731 #if defined(__cplusplus) || defined(c_plusplus)
732 }
733 #endif
734
735 MagickExport void XAnimateBackgroundImage(Display *display,
736   XResourceInfo *resource_info,Image *images,ExceptionInfo *exception)
737 {
738   char
739     geometry[MaxTextExtent],
740     visual_type[MaxTextExtent];
741
742   Image
743     *coalesce_image,
744     *display_image,
745     **image_list;
746
747   int
748     scene;
749
750   MagickStatusType
751     status;
752
753   RectangleInfo
754     geometry_info;
755
756   register ssize_t
757     i;
758
759   size_t
760     number_scenes;
761
762   static XPixelInfo
763     pixel;
764
765   static XStandardColormap
766     *map_info;
767
768   static XVisualInfo
769     *visual_info = (XVisualInfo *) NULL;
770
771   static XWindowInfo
772     window_info;
773
774   unsigned int
775     height,
776     width;
777
778   size_t
779     delay;
780
781   Window
782     root_window;
783
784   XEvent
785     event;
786
787   XGCValues
788     context_values;
789
790   XResourceInfo
791     resources;
792
793   XWindowAttributes
794     window_attributes;
795
796   /*
797     Determine target window.
798   */
799   assert(images != (Image *) NULL);
800   assert(images->signature == MagickSignature);
801   if (images->debug != MagickFalse)
802     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
803   resources=(*resource_info);
804   window_info.id=(Window) NULL;
805   root_window=XRootWindow(display,XDefaultScreen(display));
806   if (LocaleCompare(resources.window_id,"root") == 0)
807     window_info.id=root_window;
808   else
809     {
810       if (isdigit((int) ((unsigned char) *resources.window_id)) != 0)
811         window_info.id=XWindowByID(display,root_window,
812           (Window) strtol((char *) resources.window_id,(char **) NULL,0));
813       if (window_info.id == (Window) NULL)
814         window_info.id=
815           XWindowByName(display,root_window,resources.window_id);
816     }
817   if (window_info.id == (Window) NULL)
818     {
819       ThrowXWindowException(XServerError,"NoWindowWithSpecifiedIDExists",
820         resources.window_id);
821       return;
822     }
823   /*
824     Determine window visual id.
825   */
826   window_attributes.width=XDisplayWidth(display,XDefaultScreen(display));
827   window_attributes.height=XDisplayHeight(display,XDefaultScreen(display));
828   (void) CopyMagickString(visual_type,"default",MaxTextExtent);
829   status=XGetWindowAttributes(display,window_info.id,&window_attributes) != 0 ?
830     MagickTrue : MagickFalse;
831   if (status != MagickFalse)
832     (void) FormatLocaleString(visual_type,MaxTextExtent,"0x%lx",
833       XVisualIDFromVisual(window_attributes.visual));
834   if (visual_info == (XVisualInfo *) NULL)
835     {
836       /*
837         Allocate standard colormap.
838       */
839       map_info=XAllocStandardColormap();
840       if (map_info == (XStandardColormap *) NULL)
841         ThrowXWindowFatalException(ResourceLimitError,"MemoryAllocationFailed",
842           images->filename);
843       map_info->colormap=(Colormap) NULL;
844       pixel.pixels=(unsigned long *) NULL;
845       /*
846         Initialize visual info.
847       */
848       resources.map_type=(char *) NULL;
849       resources.visual_type=visual_type;
850       visual_info=XBestVisualInfo(display,map_info,&resources);
851       if (visual_info == (XVisualInfo *) NULL)
852         ThrowXWindowFatalException(XServerFatalError,"UnableToGetVisual",
853           images->filename);
854       /*
855         Initialize window info.
856       */
857       window_info.ximage=(XImage *) NULL;
858       window_info.matte_image=(XImage *) NULL;
859       window_info.pixmap=(Pixmap) NULL;
860       window_info.matte_pixmap=(Pixmap) NULL;
861     }
862   /*
863     Free previous root colors.
864   */
865   if (window_info.id == root_window)
866     XDestroyWindowColors(display,root_window);
867   coalesce_image=CoalesceImages(images,exception);
868   if (coalesce_image == (Image *) NULL)
869     ThrowXWindowFatalException(ResourceLimitError,"MemoryAllocationFailed",
870       images->filename);
871   images=coalesce_image;
872   if (resources.map_type == (char *) NULL)
873     if ((visual_info->klass != TrueColor) &&
874         (visual_info->klass != DirectColor))
875       {
876         Image
877           *next;
878
879         /*
880           Determine if the sequence of images has the identical colormap.
881         */
882         for (next=images; next != (Image *) NULL; )
883         {
884           next->matte=MagickFalse;
885           if ((next->storage_class == DirectClass) ||
886               (next->colors != images->colors) ||
887               (next->colors > (size_t) visual_info->colormap_size))
888             break;
889           for (i=0; i < (ssize_t) images->colors; i++)
890             if (IsPixelInfoEquivalent(next->colormap+i,images->colormap+i) == MagickFalse)
891               break;
892           if (i < (ssize_t) images->colors)
893             break;
894           next=GetNextImageInList(next);
895         }
896         if (next != (Image *) NULL)
897           (void) RemapImages(resources.quantize_info,images,(Image *) NULL,
898             exception);
899       }
900   /*
901     Sort images by increasing scene number.
902   */
903   number_scenes=GetImageListLength(images);
904   image_list=ImageListToArray(images,exception);
905   if (image_list == (Image **) NULL)
906     ThrowXWindowFatalException(ResourceLimitFatalError,
907       "MemoryAllocationFailed",images->filename);
908   for (i=0; i < (ssize_t) number_scenes; i++)
909     if (image_list[i]->scene == 0)
910       break;
911   if (i == (ssize_t) number_scenes)
912     qsort((void *) image_list,number_scenes,sizeof(Image *),SceneCompare);
913   /*
914     Initialize Standard Colormap.
915   */
916   resources.colormap=SharedColormap;
917   display_image=image_list[0];
918   for (scene=0; scene < (int) number_scenes; scene++)
919   {
920     if ((resource_info->map_type != (char *) NULL) ||
921         (visual_info->klass == TrueColor) ||
922         (visual_info->klass == DirectColor))
923       (void) SetImageType(image_list[scene],image_list[scene]->matte ==
924         MagickFalse ? TrueColorType : TrueColorMatteType,exception);
925     if ((display_image->columns < image_list[scene]->columns) &&
926         (display_image->rows < image_list[scene]->rows))
927       display_image=image_list[scene];
928   }
929   if ((resource_info->map_type != (char *) NULL) ||
930       (visual_info->klass == TrueColor) || (visual_info->klass == DirectColor))
931     (void) SetImageType(display_image,display_image->matte == MagickFalse ?
932       TrueColorType : TrueColorMatteType,exception);
933   XMakeStandardColormap(display,visual_info,&resources,display_image,map_info,
934     &pixel,exception);
935   /*
936     Graphic context superclass.
937   */
938   context_values.background=pixel.background_color.pixel;
939   context_values.foreground=pixel.foreground_color.pixel;
940   pixel.annotate_context=XCreateGC(display,window_info.id,(unsigned long)
941     (GCBackground | GCForeground),&context_values);
942   if (pixel.annotate_context == (GC) NULL)
943     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
944       images->filename);
945   /*
946     Initialize Image window attributes.
947   */
948   XGetWindowInfo(display,visual_info,map_info,&pixel,(XFontStruct *) NULL,
949     &resources,&window_info);
950   /*
951     Create the X image.
952   */
953   window_info.width=(unsigned int) image_list[0]->columns;
954   window_info.height=(unsigned int) image_list[0]->rows;
955   if ((image_list[0]->columns != window_info.width) ||
956       (image_list[0]->rows != window_info.height))
957     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
958       image_list[0]->filename);
959   (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>",
960     window_attributes.width,window_attributes.height);
961   geometry_info.width=window_info.width;
962   geometry_info.height=window_info.height;
963   geometry_info.x=(ssize_t) window_info.x;
964   geometry_info.y=(ssize_t) window_info.y;
965   (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
966     &geometry_info.width,&geometry_info.height);
967   window_info.width=(unsigned int) geometry_info.width;
968   window_info.height=(unsigned int) geometry_info.height;
969   window_info.x=(int) geometry_info.x;
970   window_info.y=(int) geometry_info.y;
971   status=XMakeImage(display,&resources,&window_info,image_list[0],
972     window_info.width,window_info.height,exception);
973   if (status == MagickFalse)
974     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
975       images->filename);
976   window_info.x=0;
977   window_info.y=0;
978   if (display_image->debug != MagickFalse)
979     {
980       (void) LogMagickEvent(X11Event,GetMagickModule(),
981         "Image: %s[%.20g] %.20gx%.20g ",image_list[0]->filename,(double)
982         image_list[0]->scene,(double) image_list[0]->columns,(double)
983         image_list[0]->rows);
984       if (image_list[0]->colors != 0)
985         (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
986           image_list[0]->colors);
987       (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",
988         image_list[0]->magick);
989     }
990   /*
991     Adjust image dimensions as specified by backdrop or geometry options.
992   */
993   width=window_info.width;
994   height=window_info.height;
995   if (resources.backdrop != MagickFalse)
996     {
997       /*
998         Center image on window.
999       */
1000       window_info.x=(int) (window_attributes.width/2)-
1001         (window_info.ximage->width/2);
1002       window_info.y=(int) (window_attributes.height/2)-
1003         (window_info.ximage->height/2);
1004       width=(unsigned int) window_attributes.width;
1005       height=(unsigned int) window_attributes.height;
1006     }
1007   if (resources.image_geometry != (char *) NULL)
1008     {
1009       char
1010         default_geometry[MaxTextExtent];
1011
1012       int
1013         flags,
1014         gravity;
1015
1016       XSizeHints
1017         *size_hints;
1018
1019       /*
1020         User specified geometry.
1021       */
1022       size_hints=XAllocSizeHints();
1023       if (size_hints == (XSizeHints *) NULL)
1024         ThrowXWindowFatalException(ResourceLimitFatalError,
1025           "MemoryAllocationFailed",images->filename);
1026       size_hints->flags=0L;
1027       (void) FormatLocaleString(default_geometry,MaxTextExtent,"%ux%u",width,
1028         height);
1029       flags=XWMGeometry(display,visual_info->screen,resources.image_geometry,
1030         default_geometry,window_info.border_width,size_hints,&window_info.x,
1031         &window_info.y,(int *) &width,(int *) &height,&gravity);
1032       if (((flags & (XValue | YValue))) != 0)
1033         {
1034           width=(unsigned int) window_attributes.width;
1035           height=(unsigned int) window_attributes.height;
1036         }
1037       (void) XFree((void *) size_hints);
1038     }
1039   /*
1040     Create the X pixmap.
1041   */
1042   window_info.pixmap=XCreatePixmap(display,window_info.id,(unsigned int) width,
1043     (unsigned int) height,window_info.depth);
1044   if (window_info.pixmap == (Pixmap) NULL)
1045     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXPixmap",
1046       images->filename);
1047   /*
1048     Display pixmap on the window.
1049   */
1050   if (((unsigned int) width > window_info.width) ||
1051       ((unsigned int) height > window_info.height))
1052     (void) XFillRectangle(display,window_info.pixmap,
1053       window_info.annotate_context,0,0,(unsigned int) width,
1054       (unsigned int) height);
1055   (void) XPutImage(display,window_info.pixmap,window_info.annotate_context,
1056     window_info.ximage,0,0,window_info.x,window_info.y,window_info.width,
1057     window_info.height);
1058   (void) XSetWindowBackgroundPixmap(display,window_info.id,window_info.pixmap);
1059   (void) XClearWindow(display,window_info.id);
1060   /*
1061     Initialize image pixmaps structure.
1062   */
1063   window_info.pixmaps=(Pixmap *) AcquireQuantumMemory(number_scenes,
1064     sizeof(*window_info.pixmaps));
1065   window_info.matte_pixmaps=(Pixmap *) AcquireQuantumMemory(number_scenes,
1066     sizeof(*window_info.matte_pixmaps));
1067   if ((window_info.pixmaps == (Pixmap *) NULL) ||
1068       (window_info.matte_pixmaps == (Pixmap *) NULL))
1069     ThrowXWindowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed",
1070       images->filename);
1071   window_info.pixmaps[0]=window_info.pixmap;
1072   window_info.matte_pixmaps[0]=window_info.pixmap;
1073   for (scene=1; scene < (int) number_scenes; scene++)
1074   {
1075     unsigned int
1076       columns,
1077       rows;
1078
1079     /*
1080       Create X image.
1081     */
1082     window_info.pixmap=(Pixmap) NULL;
1083     window_info.matte_pixmap=(Pixmap) NULL;
1084     if ((resources.map_type != (char *) NULL) ||
1085         (visual_info->klass == TrueColor) ||
1086         (visual_info->klass == DirectColor))
1087       if (image_list[scene]->storage_class == PseudoClass)
1088         XGetPixelInfo(display,visual_info,map_info,&resources,
1089           image_list[scene],window_info.pixel_info);
1090     columns=(unsigned int) image_list[scene]->columns;
1091     rows=(unsigned int) image_list[scene]->rows;
1092     if ((image_list[scene]->columns != columns) ||
1093         (image_list[scene]->rows != rows))
1094       ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
1095         image_list[scene]->filename);
1096     status=XMakeImage(display,&resources,&window_info,image_list[scene],
1097       columns,rows,exception);
1098     if (status == MagickFalse)
1099       ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
1100         images->filename);
1101     if (display_image->debug != MagickFalse)
1102       {
1103         (void) LogMagickEvent(X11Event,GetMagickModule(),
1104           "Image: [%.20g] %s %.20gx%.20g ",(double) image_list[scene]->scene,
1105           image_list[scene]->filename,(double) columns,(double) rows);
1106         if (image_list[scene]->colors != 0)
1107           (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
1108             image_list[scene]->colors);
1109         (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",
1110           image_list[scene]->magick);
1111       }
1112     /*
1113       Create the X pixmap.
1114     */
1115     window_info.pixmap=XCreatePixmap(display,window_info.id,width,height,
1116       window_info.depth);
1117     if (window_info.pixmap == (Pixmap) NULL)
1118       ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXPixmap",
1119         images->filename);
1120     /*
1121       Display pixmap on the window.
1122     */
1123     if ((width > window_info.width) || (height > window_info.height))
1124       (void) XFillRectangle(display,window_info.pixmap,
1125         window_info.annotate_context,0,0,width,height);
1126     (void) XPutImage(display,window_info.pixmap,window_info.annotate_context,
1127       window_info.ximage,0,0,window_info.x,window_info.y,window_info.width,
1128       window_info.height);
1129     (void) XSetWindowBackgroundPixmap(display,window_info.id,
1130       window_info.pixmap);
1131     (void) XClearWindow(display,window_info.id);
1132     window_info.pixmaps[scene]=window_info.pixmap;
1133     window_info.matte_pixmaps[scene]=window_info.matte_pixmap;
1134     if (image_list[scene]->matte)
1135       (void) XClearWindow(display,window_info.id);
1136     delay=1000*image_list[scene]->delay/MagickMax(
1137       image_list[scene]->ticks_per_second,1L);
1138     XDelay(display,resources.delay*(delay == 0 ? 10 : delay));
1139   }
1140   window_info.pixel_info=(&pixel);
1141   /*
1142     Display pixmap on the window.
1143   */
1144   (void) XSelectInput(display,window_info.id,SubstructureNotifyMask);
1145   event.type=Expose;
1146   do
1147   {
1148     for (scene=0; scene < (int) number_scenes; scene++)
1149     {
1150       if (XEventsQueued(display,QueuedAfterFlush) > 0)
1151         {
1152           (void) XNextEvent(display,&event);
1153           if (event.type == DestroyNotify)
1154             break;
1155         }
1156       window_info.pixmap=window_info.pixmaps[scene];
1157       window_info.matte_pixmap=window_info.matte_pixmaps[scene];
1158       (void) XSetWindowBackgroundPixmap(display,window_info.id,
1159         window_info.pixmap);
1160       (void) XClearWindow(display,window_info.id);
1161       (void) XSync(display,MagickFalse);
1162       delay=1000*image_list[scene]->delay/MagickMax(
1163         image_list[scene]->ticks_per_second,1L);
1164       XDelay(display,resources.delay*(delay == 0 ? 10 : delay));
1165     }
1166   } while (event.type != DestroyNotify);
1167   (void) XSync(display,MagickFalse);
1168   image_list=(Image **) RelinquishMagickMemory(image_list);
1169   images=DestroyImageList(images);
1170 }
1171 \f
1172 /*
1173 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1174 %                                                                             %
1175 %                                                                             %
1176 %                                                                             %
1177 +   X A n i m a t e I m a g e s                                               %
1178 %                                                                             %
1179 %                                                                             %
1180 %                                                                             %
1181 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1182 %
1183 %  XAnimateImages() displays an image via X11.
1184 %
1185 %  The format of the XAnimateImages method is:
1186 %
1187 %      Image *XAnimateImages(Display *display,XResourceInfo *resource_info,
1188 %        char **argv,const int argc,Image *images,ExceptionInfo *exception)
1189 %
1190 %  A description of each parameter follows:
1191 %
1192 %    o display: Specifies a connection to an X server;  returned from
1193 %      XOpenDisplay.
1194 %
1195 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
1196 %
1197 %    o argv: Specifies the application's argument list.
1198 %
1199 %    o argc: Specifies the number of arguments.
1200 %
1201 %    o images: the image list.
1202 %
1203 %    o exception: return any errors or warnings in this structure.
1204 %
1205 */
1206 MagickExport Image *XAnimateImages(Display *display,
1207   XResourceInfo *resource_info,char **argv,const int argc,Image *images,
1208   ExceptionInfo *exception)
1209 {
1210 #define MagickMenus  4
1211 #define MaXWindows  8
1212 #define MagickTitle  "Commands"
1213
1214   static const char
1215     *CommandMenu[]=
1216     {
1217       "Animate",
1218       "Speed",
1219       "Direction",
1220       "Help",
1221       "Image Info",
1222       "Quit",
1223       (char *) NULL
1224     },
1225     *AnimateMenu[]=
1226     {
1227       "Open...",
1228       "Play",
1229       "Step",
1230       "Repeat",
1231       "Auto Reverse",
1232       "Save...",
1233       (char *) NULL
1234     },
1235     *SpeedMenu[]=
1236     {
1237       "Faster",
1238       "Slower",
1239       (char *) NULL
1240     },
1241     *DirectionMenu[]=
1242     {
1243       "Forward",
1244       "Reverse",
1245       (char *) NULL
1246     },
1247     *HelpMenu[]=
1248     {
1249       "Overview",
1250       "Browse Documentation",
1251       "About Animate",
1252       (char *) NULL
1253     };
1254
1255   static const char
1256     **Menus[MagickMenus]=
1257     {
1258       AnimateMenu,
1259       SpeedMenu,
1260       DirectionMenu,
1261       HelpMenu
1262     };
1263
1264   static const CommandType
1265     CommandMenus[]=
1266     {
1267       NullCommand,
1268       NullCommand,
1269       NullCommand,
1270       NullCommand,
1271       InfoCommand,
1272       QuitCommand
1273     },
1274     CommandTypes[]=
1275     {
1276       OpenCommand,
1277       PlayCommand,
1278       StepCommand,
1279       RepeatCommand,
1280       AutoReverseCommand,
1281       SaveCommand
1282     },
1283     SpeedCommands[]=
1284     {
1285       FasterCommand,
1286       SlowerCommand
1287     },
1288     DirectionCommands[]=
1289     {
1290       ForwardCommand,
1291       ReverseCommand
1292     },
1293     HelpCommands[]=
1294     {
1295       HelpCommand,
1296       BrowseDocumentationCommand,
1297       VersionCommand
1298     };
1299
1300   static const CommandType
1301     *Commands[MagickMenus]=
1302     {
1303       CommandTypes,
1304       SpeedCommands,
1305       DirectionCommands,
1306       HelpCommands
1307     };
1308
1309   char
1310     command[MaxTextExtent],
1311     *directory,
1312     geometry[MaxTextExtent],
1313     resource_name[MaxTextExtent];
1314
1315   CommandType
1316     command_type;
1317
1318   Image
1319     *coalesce_image,
1320     *display_image,
1321     *image,
1322     **image_list,
1323     *nexus;
1324
1325   int
1326     status;
1327
1328   KeySym
1329     key_symbol;
1330
1331   MagickStatusType
1332     context_mask,
1333     state;
1334
1335   RectangleInfo
1336     geometry_info;
1337
1338   register char
1339     *p;
1340
1341   register ssize_t
1342     i;
1343
1344   ssize_t
1345     first_scene,
1346     iterations,
1347     scene;
1348
1349   static char
1350     working_directory[MaxTextExtent];
1351
1352   static size_t
1353     number_windows;
1354
1355   static XWindowInfo
1356     *magick_windows[MaXWindows];
1357
1358   time_t
1359     timestamp;
1360
1361   size_t
1362     delay,
1363     number_scenes;
1364
1365   WarningHandler
1366     warning_handler;
1367
1368   Window
1369     root_window;
1370
1371   XClassHint
1372     *class_hints;
1373
1374   XEvent
1375     event;
1376
1377   XFontStruct
1378     *font_info;
1379
1380   XGCValues
1381     context_values;
1382
1383   XPixelInfo
1384     *icon_pixel,
1385     *pixel;
1386
1387   XResourceInfo
1388     *icon_resources;
1389
1390   XStandardColormap
1391     *icon_map,
1392     *map_info;
1393
1394   XTextProperty
1395     window_name;
1396
1397   XVisualInfo
1398     *icon_visual,
1399     *visual_info;
1400
1401   XWindowChanges
1402     window_changes;
1403
1404   XWindows
1405     *windows;
1406
1407   XWMHints
1408     *manager_hints;
1409
1410   assert(images != (Image *) NULL);
1411   assert(images->signature == MagickSignature);
1412   if (images->debug != MagickFalse)
1413     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
1414   warning_handler=(WarningHandler) NULL;
1415   windows=XSetWindows((XWindows *) ~0);
1416   if (windows != (XWindows *) NULL)
1417     {
1418       int
1419         status;
1420
1421       status=chdir(working_directory);
1422       if (status == -1)
1423         (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
1424           "UnableToOpenFile","%s",working_directory);
1425       warning_handler=resource_info->display_warnings ?
1426         SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
1427       warning_handler=resource_info->display_warnings ?
1428         SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
1429     }
1430   else
1431     {
1432       register Image
1433         *p;
1434
1435       /*
1436         Initialize window structure.
1437       */
1438       for (p=images; p != (Image *) NULL; p=GetNextImageInList(p))
1439       {
1440         if (p->storage_class == DirectClass)
1441           {
1442             resource_info->colors=0;
1443             break;
1444           }
1445         if (p->colors > resource_info->colors)
1446           resource_info->colors=p->colors;
1447       }
1448       windows=XSetWindows(XInitializeWindows(display,resource_info));
1449       if (windows == (XWindows *) NULL)
1450         ThrowXWindowFatalException(XServerFatalError,"MemoryAllocationFailed",
1451           images->filename);
1452       /*
1453         Initialize window id's.
1454       */
1455       number_windows=0;
1456       magick_windows[number_windows++]=(&windows->icon);
1457       magick_windows[number_windows++]=(&windows->backdrop);
1458       magick_windows[number_windows++]=(&windows->image);
1459       magick_windows[number_windows++]=(&windows->info);
1460       magick_windows[number_windows++]=(&windows->command);
1461       magick_windows[number_windows++]=(&windows->widget);
1462       magick_windows[number_windows++]=(&windows->popup);
1463       for (i=0; i < (ssize_t) number_windows; i++)
1464         magick_windows[i]->id=(Window) NULL;
1465     }
1466   /*
1467     Initialize font info.
1468   */
1469   if (windows->font_info != (XFontStruct *) NULL)
1470     (void) XFreeFont(display,windows->font_info);
1471   windows->font_info=XBestFont(display,resource_info,MagickFalse);
1472   if (windows->font_info == (XFontStruct *) NULL)
1473     ThrowXWindowFatalException(XServerFatalError,"UnableToLoadFont",
1474       resource_info->font);
1475   /*
1476     Initialize Standard Colormap.
1477   */
1478   map_info=windows->map_info;
1479   icon_map=windows->icon_map;
1480   visual_info=windows->visual_info;
1481   icon_visual=windows->icon_visual;
1482   pixel=windows->pixel_info;
1483   icon_pixel=windows->icon_pixel;
1484   font_info=windows->font_info;
1485   icon_resources=windows->icon_resources;
1486   class_hints=windows->class_hints;
1487   manager_hints=windows->manager_hints;
1488   root_window=XRootWindow(display,visual_info->screen);
1489   coalesce_image=CoalesceImages(images,exception);
1490   if (coalesce_image == (Image *) NULL)
1491     ThrowXWindowFatalException(XServerFatalError,"MemoryAllocationFailed",
1492       images->filename);
1493   images=coalesce_image;
1494   if (resource_info->map_type == (char *) NULL)
1495     if ((visual_info->klass != TrueColor) &&
1496         (visual_info->klass != DirectColor))
1497       {
1498         Image
1499           *next;
1500
1501         /*
1502           Determine if the sequence of images has the identical colormap.
1503         */
1504         for (next=images; next != (Image *) NULL; )
1505         {
1506           next->matte=MagickFalse;
1507           if ((next->storage_class == DirectClass) ||
1508               (next->colors != images->colors) ||
1509               (next->colors > (size_t) visual_info->colormap_size))
1510             break;
1511           for (i=0; i < (ssize_t) images->colors; i++)
1512             if (IsPixelInfoEquivalent(next->colormap+i,images->colormap+i) == MagickFalse)
1513               break;
1514           if (i < (ssize_t) images->colors)
1515             break;
1516           next=GetNextImageInList(next);
1517         }
1518         if (next != (Image *) NULL)
1519           (void) RemapImages(resource_info->quantize_info,images,
1520             (Image *) NULL,exception);
1521       }
1522   /*
1523     Sort images by increasing scene number.
1524   */
1525   number_scenes=GetImageListLength(images);
1526   image_list=ImageListToArray(images,exception);
1527   if (image_list == (Image **) NULL)
1528     ThrowXWindowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed",
1529       images->filename);
1530   for (scene=0; scene < (ssize_t) number_scenes; scene++)
1531     if (image_list[scene]->scene == 0)
1532       break;
1533   if (scene == (ssize_t) number_scenes)
1534     qsort((void *) image_list,number_scenes,sizeof(Image *),SceneCompare);
1535   /*
1536     Initialize Standard Colormap.
1537   */
1538   nexus=NewImageList();
1539   display_image=image_list[0];
1540   for (scene=0; scene < (ssize_t) number_scenes; scene++)
1541   {
1542     if ((resource_info->map_type != (char *) NULL) ||
1543         (visual_info->klass == TrueColor) ||
1544         (visual_info->klass == DirectColor))
1545       (void) SetImageType(image_list[scene],image_list[scene]->matte ==
1546         MagickFalse ? TrueColorType : TrueColorMatteType,exception);
1547     if ((display_image->columns < image_list[scene]->columns) &&
1548         (display_image->rows < image_list[scene]->rows))
1549       display_image=image_list[scene];
1550   }
1551   if (display_image->debug != MagickFalse)
1552     {
1553       (void) LogMagickEvent(X11Event,GetMagickModule(),
1554         "Image: %s[%.20g] %.20gx%.20g ",display_image->filename,(double)
1555         display_image->scene,(double) display_image->columns,(double)
1556         display_image->rows);
1557       if (display_image->colors != 0)
1558         (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
1559           display_image->colors);
1560       (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",
1561         display_image->magick);
1562     }
1563   XMakeStandardColormap(display,visual_info,resource_info,display_image,
1564     map_info,pixel,exception);
1565   /*
1566     Initialize graphic context.
1567   */
1568   windows->context.id=(Window) NULL;
1569   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
1570     resource_info,&windows->context);
1571   (void) CloneString(&class_hints->res_name,resource_info->client_name);
1572   (void) CloneString(&class_hints->res_class,resource_info->client_name);
1573   class_hints->res_class[0]=(char) toupper((int) class_hints->res_class[0]);
1574   manager_hints->flags=InputHint | StateHint;
1575   manager_hints->input=MagickFalse;
1576   manager_hints->initial_state=WithdrawnState;
1577   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
1578     &windows->context);
1579   if (display_image->debug != MagickFalse)
1580     (void) LogMagickEvent(X11Event,GetMagickModule(),
1581       "Window id: 0x%lx (context)",windows->context.id);
1582   context_values.background=pixel->background_color.pixel;
1583   context_values.font=font_info->fid;
1584   context_values.foreground=pixel->foreground_color.pixel;
1585   context_values.graphics_exposures=MagickFalse;
1586   context_mask=(MagickStatusType)
1587     (GCBackground | GCFont | GCForeground | GCGraphicsExposures);
1588   if (pixel->annotate_context != (GC) NULL)
1589     (void) XFreeGC(display,pixel->annotate_context);
1590   pixel->annotate_context=
1591     XCreateGC(display,windows->context.id,context_mask,&context_values);
1592   if (pixel->annotate_context == (GC) NULL)
1593     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
1594       images->filename);
1595   context_values.background=pixel->depth_color.pixel;
1596   if (pixel->widget_context != (GC) NULL)
1597     (void) XFreeGC(display,pixel->widget_context);
1598   pixel->widget_context=
1599     XCreateGC(display,windows->context.id,context_mask,&context_values);
1600   if (pixel->widget_context == (GC) NULL)
1601     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
1602       images->filename);
1603   context_values.background=pixel->foreground_color.pixel;
1604   context_values.foreground=pixel->background_color.pixel;
1605   context_values.plane_mask=
1606     context_values.background ^ context_values.foreground;
1607   if (pixel->highlight_context != (GC) NULL)
1608     (void) XFreeGC(display,pixel->highlight_context);
1609   pixel->highlight_context=XCreateGC(display,windows->context.id,
1610     (size_t) (context_mask | GCPlaneMask),&context_values);
1611   if (pixel->highlight_context == (GC) NULL)
1612     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
1613       images->filename);
1614   (void) XDestroyWindow(display,windows->context.id);
1615   /*
1616     Initialize icon window.
1617   */
1618   XGetWindowInfo(display,icon_visual,icon_map,icon_pixel,(XFontStruct *) NULL,
1619     icon_resources,&windows->icon);
1620   windows->icon.geometry=resource_info->icon_geometry;
1621   XBestIconSize(display,&windows->icon,display_image);
1622   windows->icon.attributes.colormap=
1623     XDefaultColormap(display,icon_visual->screen);
1624   windows->icon.attributes.event_mask=ExposureMask | StructureNotifyMask;
1625   manager_hints->flags=InputHint | StateHint;
1626   manager_hints->input=MagickFalse;
1627   manager_hints->initial_state=IconicState;
1628   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
1629     &windows->icon);
1630   if (display_image->debug != MagickFalse)
1631     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (icon)",
1632       windows->icon.id);
1633   /*
1634     Initialize graphic context for icon window.
1635   */
1636   if (icon_pixel->annotate_context != (GC) NULL)
1637     (void) XFreeGC(display,icon_pixel->annotate_context);
1638   context_values.background=icon_pixel->background_color.pixel;
1639   context_values.foreground=icon_pixel->foreground_color.pixel;
1640   icon_pixel->annotate_context=XCreateGC(display,windows->icon.id,
1641     (size_t) (GCBackground | GCForeground),&context_values);
1642   if (icon_pixel->annotate_context == (GC) NULL)
1643     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
1644       images->filename);
1645   windows->icon.annotate_context=icon_pixel->annotate_context;
1646   /*
1647     Initialize Image window.
1648   */
1649   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
1650     resource_info,&windows->image);
1651   windows->image.shape=MagickTrue;  /* non-rectangular shape hint */
1652   if (resource_info->use_shared_memory == MagickFalse)
1653     windows->image.shared_memory=MagickFalse;
1654   if (resource_info->title != (char *) NULL)
1655     {
1656       char
1657         *title;
1658
1659       title=InterpretImageProperties(resource_info->image_info,display_image,
1660         resource_info->title,exception);
1661       (void) CopyMagickString(windows->image.name,title,MaxTextExtent);
1662       (void) CopyMagickString(windows->image.icon_name,title,MaxTextExtent);
1663       title=DestroyString(title);
1664     }
1665   else
1666     {
1667       char
1668         filename[MaxTextExtent];
1669
1670       /*
1671         Window name is the base of the filename.
1672       */
1673       GetPathComponent(display_image->magick_filename,TailPath,filename);
1674       (void) FormatLocaleString(windows->image.name,MaxTextExtent,
1675         "%s: %s[scene: %.20g frames: %.20g]",MagickPackageName,filename,(double)
1676         display_image->scene,(double) number_scenes);
1677       (void) CopyMagickString(windows->image.icon_name,filename,MaxTextExtent);
1678     }
1679   if (resource_info->immutable != MagickFalse)
1680     windows->image.immutable=MagickTrue;
1681   windows->image.shape=MagickTrue;
1682   windows->image.geometry=resource_info->image_geometry;
1683   (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>!",
1684     XDisplayWidth(display,visual_info->screen),
1685     XDisplayHeight(display,visual_info->screen));
1686   geometry_info.width=display_image->columns;
1687   geometry_info.height=display_image->rows;
1688   geometry_info.x=0;
1689   geometry_info.y=0;
1690   (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
1691     &geometry_info.width,&geometry_info.height);
1692   windows->image.width=(unsigned int) geometry_info.width;
1693   windows->image.height=(unsigned int) geometry_info.height;
1694   windows->image.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
1695     ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
1696     KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
1697     PropertyChangeMask | StructureNotifyMask | SubstructureNotifyMask;
1698   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
1699     resource_info,&windows->backdrop);
1700   if ((resource_info->backdrop) || (windows->backdrop.id != (Window) NULL))
1701     {
1702       /*
1703         Initialize backdrop window.
1704       */
1705       windows->backdrop.x=0;
1706       windows->backdrop.y=0;
1707       (void) CloneString(&windows->backdrop.name,"ImageMagick Backdrop");
1708       windows->backdrop.flags=(size_t) (USSize | USPosition);
1709       windows->backdrop.width=(unsigned int)
1710         XDisplayWidth(display,visual_info->screen);
1711       windows->backdrop.height=(unsigned int)
1712         XDisplayHeight(display,visual_info->screen);
1713       windows->backdrop.border_width=0;
1714       windows->backdrop.immutable=MagickTrue;
1715       windows->backdrop.attributes.do_not_propagate_mask=ButtonPressMask |
1716         ButtonReleaseMask;
1717       windows->backdrop.attributes.event_mask=ButtonPressMask | KeyPressMask |
1718         StructureNotifyMask;
1719       manager_hints->flags=IconWindowHint | InputHint | StateHint;
1720       manager_hints->icon_window=windows->icon.id;
1721       manager_hints->input=MagickTrue;
1722       manager_hints->initial_state=
1723         resource_info->iconic ? IconicState : NormalState;
1724       XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
1725         &windows->backdrop);
1726       if (display_image->debug != MagickFalse)
1727         (void) LogMagickEvent(X11Event,GetMagickModule(),
1728           "Window id: 0x%lx (backdrop)",windows->backdrop.id);
1729       (void) XMapWindow(display,windows->backdrop.id);
1730       (void) XClearWindow(display,windows->backdrop.id);
1731       if (windows->image.id != (Window) NULL)
1732         {
1733           (void) XDestroyWindow(display,windows->image.id);
1734           windows->image.id=(Window) NULL;
1735         }
1736       /*
1737         Position image in the center the backdrop.
1738       */
1739       windows->image.flags|=USPosition;
1740       windows->image.x=(XDisplayWidth(display,visual_info->screen)/2)-
1741         (windows->image.width/2);
1742       windows->image.y=(XDisplayHeight(display,visual_info->screen)/2)-
1743         (windows->image.height/2);
1744     }
1745   manager_hints->flags=IconWindowHint | InputHint | StateHint;
1746   manager_hints->icon_window=windows->icon.id;
1747   manager_hints->input=MagickTrue;
1748   manager_hints->initial_state=
1749     resource_info->iconic ? IconicState : NormalState;
1750   if (windows->group_leader.id != (Window) NULL)
1751     {
1752       /*
1753         Follow the leader.
1754       */
1755       manager_hints->flags|=(MagickStatusType) WindowGroupHint;
1756       manager_hints->window_group=windows->group_leader.id;
1757       (void) XSelectInput(display,windows->group_leader.id,StructureNotifyMask);
1758       if (display_image->debug != MagickFalse)
1759         (void) LogMagickEvent(X11Event,GetMagickModule(),
1760           "Window id: 0x%lx (group leader)",windows->group_leader.id);
1761     }
1762   XMakeWindow(display,
1763     (Window) (resource_info->backdrop ? windows->backdrop.id : root_window),
1764     argv,argc,class_hints,manager_hints,&windows->image);
1765   (void) XChangeProperty(display,windows->image.id,windows->im_protocols,
1766     XA_STRING,8,PropModeReplace,(unsigned char *) NULL,0);
1767   if (windows->group_leader.id != (Window) NULL)
1768     (void) XSetTransientForHint(display,windows->image.id,
1769       windows->group_leader.id);
1770   if (display_image->debug != MagickFalse)
1771     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (image)",
1772       windows->image.id);
1773   /*
1774     Initialize Info widget.
1775   */
1776   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
1777     resource_info,&windows->info);
1778   (void) CloneString(&windows->info.name,"Info");
1779   (void) CloneString(&windows->info.icon_name,"Info");
1780   windows->info.border_width=1;
1781   windows->info.x=2;
1782   windows->info.y=2;
1783   windows->info.flags|=PPosition;
1784   windows->info.attributes.win_gravity=UnmapGravity;
1785   windows->info.attributes.event_mask=ButtonPressMask | ExposureMask |
1786     StructureNotifyMask;
1787   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
1788   manager_hints->input=MagickFalse;
1789   manager_hints->initial_state=NormalState;
1790   manager_hints->window_group=windows->image.id;
1791   XMakeWindow(display,windows->image.id,argv,argc,class_hints,manager_hints,
1792     &windows->info);
1793   windows->info.highlight_stipple=XCreateBitmapFromData(display,
1794     windows->info.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
1795   windows->info.shadow_stipple=XCreateBitmapFromData(display,
1796     windows->info.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
1797   (void) XSetTransientForHint(display,windows->info.id,windows->image.id);
1798   if (windows->image.mapped)
1799     (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
1800   if (display_image->debug != MagickFalse)
1801     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (info)",
1802       windows->info.id);
1803   /*
1804     Initialize Command widget.
1805   */
1806   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
1807     resource_info,&windows->command);
1808   windows->command.data=MagickMenus;
1809   (void) XCommandWidget(display,windows,CommandMenu,(XEvent *) NULL);
1810   (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.command",
1811     resource_info->client_name);
1812   windows->command.geometry=XGetResourceClass(resource_info->resource_database,
1813     resource_name,"geometry",(char *) NULL);
1814   (void) CloneString(&windows->command.name,MagickTitle);
1815   windows->command.border_width=0;
1816   windows->command.flags|=PPosition;
1817   windows->command.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
1818     ButtonReleaseMask | EnterWindowMask | ExposureMask | LeaveWindowMask |
1819     OwnerGrabButtonMask | StructureNotifyMask;
1820   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
1821   manager_hints->input=MagickTrue;
1822   manager_hints->initial_state=NormalState;
1823   manager_hints->window_group=windows->image.id;
1824   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
1825     &windows->command);
1826   windows->command.highlight_stipple=XCreateBitmapFromData(display,
1827     windows->command.id,(char *) HighlightBitmap,HighlightWidth,
1828     HighlightHeight);
1829   windows->command.shadow_stipple=XCreateBitmapFromData(display,
1830     windows->command.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
1831   (void) XSetTransientForHint(display,windows->command.id,windows->image.id);
1832   if (display_image->debug != MagickFalse)
1833     (void) LogMagickEvent(X11Event,GetMagickModule(),
1834       "Window id: 0x%lx (command)",windows->command.id);
1835   /*
1836     Initialize Widget window.
1837   */
1838   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
1839     resource_info,&windows->widget);
1840   (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.widget",
1841     resource_info->client_name);
1842   windows->widget.geometry=XGetResourceClass(resource_info->resource_database,
1843     resource_name,"geometry",(char *) NULL);
1844   windows->widget.border_width=0;
1845   windows->widget.flags|=PPosition;
1846   windows->widget.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
1847     ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
1848     KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
1849     StructureNotifyMask;
1850   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
1851   manager_hints->input=MagickTrue;
1852   manager_hints->initial_state=NormalState;
1853   manager_hints->window_group=windows->image.id;
1854   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
1855     &windows->widget);
1856   windows->widget.highlight_stipple=XCreateBitmapFromData(display,
1857     windows->widget.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
1858   windows->widget.shadow_stipple=XCreateBitmapFromData(display,
1859     windows->widget.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
1860   (void) XSetTransientForHint(display,windows->widget.id,windows->image.id);
1861   if (display_image->debug != MagickFalse)
1862     (void) LogMagickEvent(X11Event,GetMagickModule(),
1863       "Window id: 0x%lx (widget)",windows->widget.id);
1864   /*
1865     Initialize popup window.
1866   */
1867   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
1868     resource_info,&windows->popup);
1869   windows->popup.border_width=0;
1870   windows->popup.flags|=PPosition;
1871   windows->popup.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
1872     ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
1873     KeyReleaseMask | LeaveWindowMask | StructureNotifyMask;
1874   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
1875   manager_hints->input=MagickTrue;
1876   manager_hints->initial_state=NormalState;
1877   manager_hints->window_group=windows->image.id;
1878   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
1879     &windows->popup);
1880   windows->popup.highlight_stipple=XCreateBitmapFromData(display,
1881     windows->popup.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
1882   windows->popup.shadow_stipple=XCreateBitmapFromData(display,
1883     windows->popup.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
1884   (void) XSetTransientForHint(display,windows->popup.id,windows->image.id);
1885   if (display_image->debug != MagickFalse)
1886     (void) LogMagickEvent(X11Event,GetMagickModule(),
1887       "Window id: 0x%lx (pop up)",windows->popup.id);
1888   /*
1889     Set out progress and warning handlers.
1890   */
1891   if (warning_handler == (WarningHandler) NULL)
1892     {
1893       warning_handler=resource_info->display_warnings ?
1894         SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
1895       warning_handler=resource_info->display_warnings ?
1896         SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
1897     }
1898   /*
1899     Initialize X image structure.
1900   */
1901   windows->image.x=0;
1902   windows->image.y=0;
1903   /*
1904     Initialize image pixmaps structure.
1905   */
1906   window_changes.width=(int) windows->image.width;
1907   window_changes.height=(int) windows->image.height;
1908   (void) XReconfigureWMWindow(display,windows->image.id,windows->command.screen,
1909     (unsigned int) (CWWidth | CWHeight),&window_changes);
1910   windows->image.pixmaps=(Pixmap *) AcquireQuantumMemory(number_scenes,
1911     sizeof(*windows->image.pixmaps));
1912   windows->image.matte_pixmaps=(Pixmap *) AcquireQuantumMemory(number_scenes,
1913     sizeof(*windows->image.pixmaps));
1914   if ((windows->image.pixmaps == (Pixmap *) NULL) ||
1915       (windows->image.matte_pixmaps == (Pixmap *) NULL))
1916     ThrowXWindowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed",
1917       images->filename);
1918   if ((windows->image.mapped == MagickFalse) ||
1919       (windows->backdrop.id != (Window) NULL))
1920     (void) XMapWindow(display,windows->image.id);
1921   XSetCursorState(display,windows,MagickTrue);
1922   for (scene=0; scene < (ssize_t) number_scenes; scene++)
1923   {
1924     unsigned int
1925       columns,
1926       rows;
1927
1928     /*
1929       Create X image.
1930     */
1931     (void) TransformImageColorspace(image_list[scene],RGBColorspace,exception);
1932     windows->image.pixmap=(Pixmap) NULL;
1933     windows->image.matte_pixmap=(Pixmap) NULL;
1934     if ((resource_info->map_type != (char *) NULL) ||
1935         (visual_info->klass == TrueColor) ||
1936         (visual_info->klass == DirectColor))
1937       if (image_list[scene]->storage_class == PseudoClass)
1938         XGetPixelInfo(display,visual_info,map_info,resource_info,
1939           image_list[scene],windows->image.pixel_info);
1940     columns=(unsigned int) image_list[scene]->columns;
1941     rows=(unsigned int) image_list[scene]->rows;
1942     if ((image_list[scene]->columns != columns) ||
1943         (image_list[scene]->rows != rows))
1944       ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
1945         image_list[scene]->filename);
1946     status=XMakeImage(display,resource_info,&windows->image,image_list[scene],
1947       columns,rows,exception);
1948     if (status == MagickFalse)
1949       ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
1950         images->filename);
1951     if (image_list[scene]->debug != MagickFalse)
1952       {
1953         (void) LogMagickEvent(X11Event,GetMagickModule(),
1954           "Image: [%.20g] %s %.20gx%.20g ",(double) image_list[scene]->scene,
1955           image_list[scene]->filename,(double) columns,(double) rows);
1956         if (image_list[scene]->colors != 0)
1957           (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
1958             image_list[scene]->colors);
1959         (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",
1960           image_list[scene]->magick);
1961       }
1962     /*
1963       Window name is the base of the filename.
1964     */
1965     if (resource_info->title != (char *) NULL)
1966       {
1967         char
1968           *title;
1969
1970         title=InterpretImageProperties(resource_info->image_info,
1971           image_list[scene],resource_info->title,exception);
1972         (void) CopyMagickString(windows->image.name,title,MaxTextExtent);
1973         title=DestroyString(title);
1974       }
1975     else
1976       {
1977         p=image_list[scene]->magick_filename+
1978           strlen(image_list[scene]->magick_filename)-1;
1979         while ((p > image_list[scene]->magick_filename) && (*(p-1) != '/'))
1980           p--;
1981         (void) FormatLocaleString(windows->image.name,MaxTextExtent,
1982           "%s: %s[%.20g of %.20g]",MagickPackageName,p,(double) scene+1,
1983           (double) number_scenes);
1984       }
1985     status=XStringListToTextProperty(&windows->image.name,1,&window_name);
1986     if (status != Success)
1987       {
1988         XSetWMName(display,windows->image.id,&window_name);
1989         (void) XFree((void *) window_name.value);
1990       }
1991     windows->image.pixmaps[scene]=windows->image.pixmap;
1992     windows->image.matte_pixmaps[scene]=windows->image.matte_pixmap;
1993     if (scene == 0)
1994       {
1995         event.xexpose.x=0;
1996         event.xexpose.y=0;
1997         event.xexpose.width=(int) image_list[scene]->columns;
1998         event.xexpose.height=(int) image_list[scene]->rows;
1999         XRefreshWindow(display,&windows->image,&event);
2000         (void) XSync(display,MagickFalse);
2001     }
2002   }
2003   XSetCursorState(display,windows,MagickFalse);
2004   if (windows->command.mapped)
2005     (void) XMapRaised(display,windows->command.id);
2006   /*
2007     Respond to events.
2008   */
2009   nexus=NewImageList();
2010   scene=0;
2011   first_scene=0;
2012   iterations=0;
2013   image=image_list[0];
2014   state=(MagickStatusType) (ForwardAnimationState | RepeatAnimationState);
2015   (void) XMagickCommand(display,resource_info,windows,PlayCommand,&images,
2016     &state,exception);
2017   do
2018   {
2019     if (XEventsQueued(display,QueuedAfterFlush) == 0)
2020       if ((state & PlayAnimationState) || (state & StepAnimationState))
2021         {
2022           MagickBooleanType
2023             pause;
2024
2025           pause=MagickFalse;
2026           delay=1000*image->delay/MagickMax(image->ticks_per_second,1L);
2027           XDelay(display,resource_info->delay*(delay == 0 ? 10 : delay));
2028           if (state & ForwardAnimationState)
2029             {
2030               /*
2031                 Forward animation:  increment scene number.
2032               */
2033               if (scene < ((ssize_t) number_scenes-1))
2034                 scene++;
2035               else
2036                 {
2037                   iterations++;
2038                   if (iterations == (ssize_t) image_list[0]->iterations)
2039                     {
2040                       iterations=0;
2041                       state|=ExitState;
2042                     }
2043                   if ((state & AutoReverseAnimationState) != 0)
2044                     {
2045                       state&=(~ForwardAnimationState);
2046                       scene--;
2047                     }
2048                   else
2049                     {
2050                       if ((state & RepeatAnimationState) == 0)
2051                         state&=(~PlayAnimationState);
2052                       scene=first_scene;
2053                       pause=MagickTrue;
2054                     }
2055                 }
2056             }
2057           else
2058             {
2059               /*
2060                 Reverse animation:  decrement scene number.
2061               */
2062               if (scene > first_scene)
2063                 scene--;
2064               else
2065                 {
2066                   iterations++;
2067                   if (iterations == (ssize_t) image_list[0]->iterations)
2068                     {
2069                       iterations=0;
2070                       state&=(~RepeatAnimationState);
2071                     }
2072                   if (state & AutoReverseAnimationState)
2073                     {
2074                       state|=ForwardAnimationState;
2075                       scene=first_scene;
2076                       pause=MagickTrue;
2077                     }
2078                   else
2079                     {
2080                       if ((state & RepeatAnimationState) == MagickFalse)
2081                         state&=(~PlayAnimationState);
2082                       scene=(ssize_t) number_scenes-1;
2083                     }
2084                 }
2085             }
2086           scene=MagickMax(scene,0);
2087           image=image_list[scene];
2088           if ((image != (Image *) NULL) && (image->start_loop != 0))
2089             first_scene=scene;
2090           if ((state & StepAnimationState) ||
2091               (resource_info->title != (char *) NULL))
2092             {
2093               /*
2094                 Update window title.
2095               */
2096               p=image_list[scene]->filename+
2097                 strlen(image_list[scene]->filename)-1;
2098               while ((p > image_list[scene]->filename) && (*(p-1) != '/'))
2099                 p--;
2100               (void) FormatLocaleString(windows->image.name,MaxTextExtent,
2101                 "%s: %s[%.20g of %.20g]",MagickPackageName,p,(double)
2102                 scene+1,(double) number_scenes);
2103               if (resource_info->title != (char *) NULL)
2104                 {
2105                   char
2106                     *title;
2107
2108                   title=InterpretImageProperties(resource_info->image_info,
2109                     image,resource_info->title,exception);
2110                   (void) CopyMagickString(windows->image.name,title,
2111                     MaxTextExtent);
2112                   title=DestroyString(title);
2113                 }
2114               status=XStringListToTextProperty(&windows->image.name,1,
2115                 &window_name);
2116               if (status != Success)
2117                 {
2118                   XSetWMName(display,windows->image.id,&window_name);
2119                   (void) XFree((void *) window_name.value);
2120                 }
2121             }
2122           /*
2123             Copy X pixmap to Image window.
2124           */
2125           XGetPixelInfo(display,visual_info,map_info,resource_info,
2126             image_list[scene],windows->image.pixel_info);
2127           windows->image.ximage->width=(int) image->columns;
2128           windows->image.ximage->height=(int) image->rows;
2129           windows->image.pixmap=windows->image.pixmaps[scene];
2130           windows->image.matte_pixmap=windows->image.matte_pixmaps[scene];
2131           event.xexpose.x=0;
2132           event.xexpose.y=0;
2133           event.xexpose.width=(int) image->columns;
2134           event.xexpose.height=(int) image->rows;
2135           if ((state & ExitState) == 0)
2136             {
2137               XRefreshWindow(display,&windows->image,&event);
2138               (void) XSync(display,MagickFalse);
2139             }
2140           state&=(~StepAnimationState);
2141           if (pause != MagickFalse)
2142             for (i=0; i < (ssize_t) resource_info->pause; i++)
2143             {
2144               int
2145                 status;
2146
2147               status=XCheckTypedWindowEvent(display,windows->image.id,KeyPress,
2148                 &event);
2149               if (status != 0)
2150                 {
2151                   int
2152                     length;
2153
2154                   length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
2155                     sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2156                   *(command+length)='\0';
2157                   if ((key_symbol == XK_q) || (key_symbol == XK_Escape))
2158                     {
2159                       XClientMessage(display,windows->image.id,
2160                         windows->im_protocols,windows->im_exit,CurrentTime);
2161                       break;
2162                     }
2163                 }
2164               (void) sleep(1);
2165             }
2166           continue;
2167         }
2168     /*
2169       Handle a window event.
2170     */
2171     timestamp=time((time_t *) NULL);
2172     (void) XNextEvent(display,&event);
2173     if (windows->image.stasis == MagickFalse)
2174       windows->image.stasis=(time((time_t *) NULL)-timestamp) > 0 ?
2175         MagickTrue : MagickFalse;
2176     if (event.xany.window == windows->command.id)
2177       {
2178         int
2179           id;
2180
2181         /*
2182           Select a command from the Command widget.
2183         */
2184         id=XCommandWidget(display,windows,CommandMenu,&event);
2185         if (id < 0)
2186           continue;
2187         (void) CopyMagickString(command,CommandMenu[id],MaxTextExtent);
2188         command_type=CommandMenus[id];
2189         if (id < MagickMenus)
2190           {
2191             int
2192               entry;
2193
2194             /*
2195               Select a command from a pop-up menu.
2196             */
2197             entry=XMenuWidget(display,windows,CommandMenu[id],Menus[id],
2198               command);
2199             if (entry < 0)
2200               continue;
2201             (void) CopyMagickString(command,Menus[id][entry],MaxTextExtent);
2202             command_type=Commands[id][entry];
2203           }
2204         if (command_type != NullCommand)
2205           nexus=XMagickCommand(display,resource_info,windows,
2206             command_type,&image,&state,exception);
2207         continue;
2208       }
2209     switch (event.type)
2210     {
2211       case ButtonPress:
2212       {
2213         if (display_image->debug != MagickFalse)
2214           (void) LogMagickEvent(X11Event,GetMagickModule(),
2215             "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
2216             event.xbutton.button,event.xbutton.x,event.xbutton.y);
2217         if ((event.xbutton.button == Button3) &&
2218             (event.xbutton.state & Mod1Mask))
2219           {
2220             /*
2221               Convert Alt-Button3 to Button2.
2222             */
2223             event.xbutton.button=Button2;
2224             event.xbutton.state&=(~Mod1Mask);
2225           }
2226         if (event.xbutton.window == windows->backdrop.id)
2227           {
2228             (void) XSetInputFocus(display,event.xbutton.window,RevertToParent,
2229               event.xbutton.time);
2230             break;
2231           }
2232         if (event.xbutton.window == windows->image.id)
2233           {
2234             if (resource_info->immutable != MagickFalse)
2235               {
2236                 state|=ExitState;
2237                 break;
2238               }
2239             /*
2240               Map/unmap Command widget.
2241             */
2242             if (windows->command.mapped)
2243               (void) XWithdrawWindow(display,windows->command.id,
2244                 windows->command.screen);
2245             else
2246               {
2247                 (void) XCommandWidget(display,windows,CommandMenu,
2248                   (XEvent *) NULL);
2249                 (void) XMapRaised(display,windows->command.id);
2250               }
2251           }
2252         break;
2253       }
2254       case ButtonRelease:
2255       {
2256         if (display_image->debug != MagickFalse)
2257           (void) LogMagickEvent(X11Event,GetMagickModule(),
2258             "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
2259             event.xbutton.button,event.xbutton.x,event.xbutton.y);
2260         break;
2261       }
2262       case ClientMessage:
2263       {
2264         if (display_image->debug != MagickFalse)
2265           (void) LogMagickEvent(X11Event,GetMagickModule(),
2266             "Client Message: 0x%lx 0x%lx %d 0x%lx",(unsigned long)
2267             event.xclient.window,(unsigned long) event.xclient.message_type,
2268             event.xclient.format,(unsigned long) event.xclient.data.l[0]);
2269         if (event.xclient.message_type == windows->im_protocols)
2270           {
2271             if (*event.xclient.data.l == (long) windows->im_update_colormap)
2272               {
2273                 /*
2274                   Update graphic context and window colormap.
2275                 */
2276                 for (i=0; i < (ssize_t) number_windows; i++)
2277                 {
2278                   if (magick_windows[i]->id == windows->icon.id)
2279                     continue;
2280                   context_values.background=pixel->background_color.pixel;
2281                   context_values.foreground=pixel->foreground_color.pixel;
2282                   (void) XChangeGC(display,magick_windows[i]->annotate_context,
2283                     context_mask,&context_values);
2284                   (void) XChangeGC(display,magick_windows[i]->widget_context,
2285                     context_mask,&context_values);
2286                   context_values.background=pixel->foreground_color.pixel;
2287                   context_values.foreground=pixel->background_color.pixel;
2288                   context_values.plane_mask=
2289                     context_values.background ^ context_values.foreground;
2290                   (void) XChangeGC(display,magick_windows[i]->highlight_context,
2291                     (size_t) (context_mask | GCPlaneMask),
2292                     &context_values);
2293                   magick_windows[i]->attributes.background_pixel=
2294                     pixel->background_color.pixel;
2295                   magick_windows[i]->attributes.border_pixel=
2296                     pixel->border_color.pixel;
2297                   magick_windows[i]->attributes.colormap=map_info->colormap;
2298                   (void) XChangeWindowAttributes(display,magick_windows[i]->id,
2299                     (unsigned long) magick_windows[i]->mask,
2300                     &magick_windows[i]->attributes);
2301                 }
2302                 if (windows->backdrop.id != (Window) NULL)
2303                   (void) XInstallColormap(display,map_info->colormap);
2304                 break;
2305               }
2306             if (*event.xclient.data.l == (long) windows->im_exit)
2307               {
2308                 state|=ExitState;
2309                 break;
2310               }
2311             break;
2312           }
2313         if (event.xclient.message_type == windows->dnd_protocols)
2314           {
2315             Atom
2316               selection,
2317               type;
2318
2319             int
2320               format,
2321               status;
2322
2323             unsigned char
2324               *data;
2325
2326             unsigned long
2327               after,
2328               length;
2329
2330             /*
2331               Display image named by the Drag-and-Drop selection.
2332             */
2333             if ((*event.xclient.data.l != 2) && (*event.xclient.data.l != 128))
2334               break;
2335             selection=XInternAtom(display,"DndSelection",MagickFalse);
2336             status=XGetWindowProperty(display,root_window,selection,0L,2047L,
2337               MagickFalse,(Atom) AnyPropertyType,&type,&format,&length,&after,
2338               &data);
2339             if ((status != Success) || (length == 0))
2340               break;
2341             if (*event.xclient.data.l == 2)
2342               {
2343                 /*
2344                   Offix DND.
2345                 */
2346                 (void) CopyMagickString(resource_info->image_info->filename,
2347                   (char *) data,MaxTextExtent);
2348               }
2349             else
2350               {
2351                 /*
2352                   XDND.
2353                 */
2354                 if (LocaleNCompare((char *) data,"file:",5) != 0)
2355                   {
2356                     (void) XFree((void *) data);
2357                     break;
2358                   }
2359                 (void) CopyMagickString(resource_info->image_info->filename,
2360                   ((char *) data)+5,MaxTextExtent);
2361               }
2362             nexus=ReadImage(resource_info->image_info,exception);
2363             CatchException(exception);
2364             if (nexus != (Image *) NULL)
2365               state|=ExitState;
2366             (void) XFree((void *) data);
2367             break;
2368           }
2369         /*
2370           If client window delete message, exit.
2371         */
2372         if (event.xclient.message_type != windows->wm_protocols)
2373           break;
2374         if (*event.xclient.data.l == (long) windows->wm_take_focus)
2375           {
2376             (void) XSetInputFocus(display,event.xclient.window,RevertToParent,
2377               (Time) event.xclient.data.l[1]);
2378             break;
2379           }
2380         if (*event.xclient.data.l != (long) windows->wm_delete_window)
2381           break;
2382         (void) XWithdrawWindow(display,event.xclient.window,
2383           visual_info->screen);
2384         if (event.xclient.window == windows->image.id)
2385           {
2386             state|=ExitState;
2387             break;
2388           }
2389         break;
2390       }
2391       case ConfigureNotify:
2392       {
2393         if (display_image->debug != MagickFalse)
2394           (void) LogMagickEvent(X11Event,GetMagickModule(),
2395             "Configure Notify: 0x%lx %dx%d+%d+%d %d",event.xconfigure.window,
2396             event.xconfigure.width,event.xconfigure.height,event.xconfigure.x,
2397             event.xconfigure.y,event.xconfigure.send_event);
2398         if (event.xconfigure.window == windows->image.id)
2399           {
2400             if (event.xconfigure.send_event != 0)
2401               {
2402                 XWindowChanges
2403                   window_changes;
2404
2405                 /*
2406                   Position the transient windows relative of the Image window.
2407                 */
2408                 if (windows->command.geometry == (char *) NULL)
2409                   if (windows->command.mapped == MagickFalse)
2410                     {
2411                        windows->command.x=
2412                           event.xconfigure.x-windows->command.width-25;
2413                         windows->command.y=event.xconfigure.y;
2414                         XConstrainWindowPosition(display,&windows->command);
2415                         window_changes.x=windows->command.x;
2416                         window_changes.y=windows->command.y;
2417                         (void) XReconfigureWMWindow(display,windows->command.id,
2418                           windows->command.screen,(unsigned int) (CWX | CWY),
2419                           &window_changes);
2420                     }
2421                 if (windows->widget.geometry == (char *) NULL)
2422                   if (windows->widget.mapped == MagickFalse)
2423                     {
2424                       windows->widget.x=
2425                         event.xconfigure.x+event.xconfigure.width/10;
2426                       windows->widget.y=
2427                         event.xconfigure.y+event.xconfigure.height/10;
2428                       XConstrainWindowPosition(display,&windows->widget);
2429                       window_changes.x=windows->widget.x;
2430                       window_changes.y=windows->widget.y;
2431                       (void) XReconfigureWMWindow(display,windows->widget.id,
2432                         windows->widget.screen,(unsigned int) (CWX | CWY),
2433                         &window_changes);
2434                     }
2435               }
2436             /*
2437               Image window has a new configuration.
2438             */
2439             windows->image.width=(unsigned int) event.xconfigure.width;
2440             windows->image.height=(unsigned int) event.xconfigure.height;
2441             break;
2442           }
2443         if (event.xconfigure.window == windows->icon.id)
2444           {
2445             /*
2446               Icon window has a new configuration.
2447             */
2448             windows->icon.width=(unsigned int) event.xconfigure.width;
2449             windows->icon.height=(unsigned int) event.xconfigure.height;
2450             break;
2451           }
2452         break;
2453       }
2454       case DestroyNotify:
2455       {
2456         /*
2457           Group leader has exited.
2458         */
2459         if (display_image->debug != MagickFalse)
2460           (void) LogMagickEvent(X11Event,GetMagickModule(),
2461             "Destroy Notify: 0x%lx",event.xdestroywindow.window);
2462         if (event.xdestroywindow.window == windows->group_leader.id)
2463           {
2464             state|=ExitState;
2465             break;
2466           }
2467         break;
2468       }
2469       case EnterNotify:
2470       {
2471         /*
2472           Selectively install colormap.
2473         */
2474         if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
2475           if (event.xcrossing.mode != NotifyUngrab)
2476             XInstallColormap(display,map_info->colormap);
2477         break;
2478       }
2479       case Expose:
2480       {
2481         if (display_image->debug != MagickFalse)
2482           (void) LogMagickEvent(X11Event,GetMagickModule(),
2483             "Expose: 0x%lx %dx%d+%d+%d",event.xexpose.window,
2484             event.xexpose.width,event.xexpose.height,event.xexpose.x,
2485             event.xexpose.y);
2486         /*
2487           Repaint windows that are now exposed.
2488         */
2489         if (event.xexpose.window == windows->image.id)
2490           {
2491             windows->image.pixmap=windows->image.pixmaps[scene];
2492             windows->image.matte_pixmap=windows->image.matte_pixmaps[scene];
2493             XRefreshWindow(display,&windows->image,&event);
2494             break;
2495           }
2496         if (event.xexpose.window == windows->icon.id)
2497           if (event.xexpose.count == 0)
2498             {
2499               XRefreshWindow(display,&windows->icon,&event);
2500               break;
2501             }
2502         break;
2503       }
2504       case KeyPress:
2505       {
2506         static int
2507           length;
2508
2509         /*
2510           Respond to a user key press.
2511         */
2512         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
2513           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2514         *(command+length)='\0';
2515         if (display_image->debug != MagickFalse)
2516           (void) LogMagickEvent(X11Event,GetMagickModule(),
2517             "Key press: 0x%lx (%c)",(unsigned long) key_symbol,*command);
2518         command_type=NullCommand;
2519         switch (key_symbol)
2520         {
2521           case XK_o:
2522           {
2523             if ((event.xkey.state & ControlMask) == MagickFalse)
2524               break;
2525             command_type=OpenCommand;
2526             break;
2527           }
2528           case XK_BackSpace:
2529           {
2530             command_type=StepBackwardCommand;
2531             break;
2532           }
2533           case XK_space:
2534           {
2535             command_type=StepForwardCommand;
2536             break;
2537           }
2538           case XK_less:
2539           {
2540             command_type=FasterCommand;
2541             break;
2542           }
2543           case XK_greater:
2544           {
2545             command_type=SlowerCommand;
2546             break;
2547           }
2548           case XK_F1:
2549           {
2550             command_type=HelpCommand;
2551             break;
2552           }
2553           case XK_Find:
2554           {
2555             command_type=BrowseDocumentationCommand;
2556             break;
2557           }
2558           case XK_question:
2559           {
2560             command_type=InfoCommand;
2561             break;
2562           }
2563           case XK_q:
2564           case XK_Escape:
2565           {
2566             command_type=QuitCommand;
2567             break;
2568           }
2569           default:
2570             break;
2571         }
2572         if (command_type != NullCommand)
2573           nexus=XMagickCommand(display,resource_info,windows,
2574             command_type,&image,&state,exception);
2575         break;
2576       }
2577       case KeyRelease:
2578       {
2579         /*
2580           Respond to a user key release.
2581         */
2582         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2583           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2584         if (display_image->debug != MagickFalse)
2585           (void) LogMagickEvent(X11Event,GetMagickModule(),
2586             "Key release: 0x%lx (%c)",(unsigned long) key_symbol,*command);
2587         break;
2588       }
2589       case LeaveNotify:
2590       {
2591         /*
2592           Selectively uninstall colormap.
2593         */
2594         if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
2595           if (event.xcrossing.mode != NotifyUngrab)
2596             XUninstallColormap(display,map_info->colormap);
2597         break;
2598       }
2599       case MapNotify:
2600       {
2601         if (display_image->debug != MagickFalse)
2602           (void) LogMagickEvent(X11Event,GetMagickModule(),"Map Notify: 0x%lx",
2603             event.xmap.window);
2604         if (event.xmap.window == windows->backdrop.id)
2605           {
2606             (void) XSetInputFocus(display,event.xmap.window,RevertToParent,
2607               CurrentTime);
2608             windows->backdrop.mapped=MagickTrue;
2609             break;
2610           }
2611         if (event.xmap.window == windows->image.id)
2612           {
2613             if (windows->backdrop.id != (Window) NULL)
2614               (void) XInstallColormap(display,map_info->colormap);
2615             if (LocaleCompare(image_list[0]->magick,"LOGO") == 0)
2616               {
2617                 if (LocaleCompare(display_image->filename,"LOGO") == 0)
2618                   nexus=XMagickCommand(display,resource_info,windows,
2619                     OpenCommand,&image,&state,exception);
2620                 else
2621                   state|=ExitState;
2622               }
2623             windows->image.mapped=MagickTrue;
2624             break;
2625           }
2626         if (event.xmap.window == windows->info.id)
2627           {
2628             windows->info.mapped=MagickTrue;
2629             break;
2630           }
2631         if (event.xmap.window == windows->icon.id)
2632           {
2633             /*
2634               Create an icon image.
2635             */
2636             XMakeStandardColormap(display,icon_visual,icon_resources,
2637               display_image,icon_map,icon_pixel,exception);
2638             (void) XMakeImage(display,icon_resources,&windows->icon,
2639               display_image,windows->icon.width,windows->icon.height,
2640               exception);
2641             (void) XSetWindowBackgroundPixmap(display,windows->icon.id,
2642               windows->icon.pixmap);
2643             (void) XClearWindow(display,windows->icon.id);
2644             (void) XWithdrawWindow(display,windows->info.id,
2645               windows->info.screen);
2646             windows->icon.mapped=MagickTrue;
2647             break;
2648           }
2649         if (event.xmap.window == windows->command.id)
2650           {
2651             windows->command.mapped=MagickTrue;
2652             break;
2653           }
2654         if (event.xmap.window == windows->popup.id)
2655           {
2656             windows->popup.mapped=MagickTrue;
2657             break;
2658           }
2659         if (event.xmap.window == windows->widget.id)
2660           {
2661             windows->widget.mapped=MagickTrue;
2662             break;
2663           }
2664         break;
2665       }
2666       case MappingNotify:
2667       {
2668         (void) XRefreshKeyboardMapping(&event.xmapping);
2669         break;
2670       }
2671       case NoExpose:
2672         break;
2673       case PropertyNotify:
2674       {
2675         Atom
2676           type;
2677
2678         int
2679           format,
2680           status;
2681
2682         unsigned char
2683           *data;
2684
2685         unsigned long
2686           after,
2687           length;
2688
2689         if (display_image->debug != MagickFalse)
2690           (void) LogMagickEvent(X11Event,GetMagickModule(),
2691             "Property Notify: 0x%lx 0x%lx %d",(unsigned long)
2692             event.xproperty.window,(unsigned long) event.xproperty.atom,
2693             event.xproperty.state);
2694         if (event.xproperty.atom != windows->im_remote_command)
2695           break;
2696         /*
2697           Display image named by the remote command protocol.
2698         */
2699         status=XGetWindowProperty(display,event.xproperty.window,
2700           event.xproperty.atom,0L,(long) MaxTextExtent,MagickFalse,(Atom)
2701           AnyPropertyType,&type,&format,&length,&after,&data);
2702         if ((status != Success) || (length == 0))
2703           break;
2704         (void) CopyMagickString(resource_info->image_info->filename,
2705           (char *) data,MaxTextExtent);
2706         nexus=ReadImage(resource_info->image_info,exception);
2707         CatchException(exception);
2708         if (nexus != (Image *) NULL)
2709           state|=ExitState;
2710         (void) XFree((void *) data);
2711         break;
2712       }
2713       case ReparentNotify:
2714       {
2715         if (display_image->debug != MagickFalse)
2716           (void) LogMagickEvent(X11Event,GetMagickModule(),
2717             "Reparent Notify: 0x%lx=>0x%lx",event.xreparent.parent,
2718             event.xreparent.window);
2719         break;
2720       }
2721       case UnmapNotify:
2722       {
2723         if (display_image->debug != MagickFalse)
2724           (void) LogMagickEvent(X11Event,GetMagickModule(),
2725             "Unmap Notify: 0x%lx",event.xunmap.window);
2726         if (event.xunmap.window == windows->backdrop.id)
2727           {
2728             windows->backdrop.mapped=MagickFalse;
2729             break;
2730           }
2731         if (event.xunmap.window == windows->image.id)
2732           {
2733             windows->image.mapped=MagickFalse;
2734             break;
2735           }
2736         if (event.xunmap.window == windows->info.id)
2737           {
2738             windows->info.mapped=MagickFalse;
2739             break;
2740           }
2741         if (event.xunmap.window == windows->icon.id)
2742           {
2743             if (map_info->colormap == icon_map->colormap)
2744               XConfigureImageColormap(display,resource_info,windows,
2745                 display_image,exception);
2746             (void) XFreeStandardColormap(display,icon_visual,icon_map,
2747               icon_pixel);
2748             windows->icon.mapped=MagickFalse;
2749             break;
2750           }
2751         if (event.xunmap.window == windows->command.id)
2752           {
2753             windows->command.mapped=MagickFalse;
2754             break;
2755           }
2756         if (event.xunmap.window == windows->popup.id)
2757           {
2758             if (windows->backdrop.id != (Window) NULL)
2759               (void) XSetInputFocus(display,windows->image.id,RevertToParent,
2760                 CurrentTime);
2761             windows->popup.mapped=MagickFalse;
2762             break;
2763           }
2764         if (event.xunmap.window == windows->widget.id)
2765           {
2766             if (windows->backdrop.id != (Window) NULL)
2767               (void) XSetInputFocus(display,windows->image.id,RevertToParent,
2768                 CurrentTime);
2769             windows->widget.mapped=MagickFalse;
2770             break;
2771           }
2772         break;
2773       }
2774       default:
2775       {
2776         if (display_image->debug != MagickFalse)
2777           (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
2778             event.type);
2779         break;
2780       }
2781     }
2782   }
2783   while (!(state & ExitState));
2784   image_list=(Image **) RelinquishMagickMemory(image_list);
2785   images=DestroyImageList(images);
2786   if ((windows->visual_info->klass == GrayScale) ||
2787       (windows->visual_info->klass == PseudoColor) ||
2788       (windows->visual_info->klass == DirectColor))
2789     {
2790       /*
2791         Withdraw windows.
2792       */
2793       if (windows->info.mapped)
2794         (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
2795       if (windows->command.mapped)
2796         (void) XWithdrawWindow(display,windows->command.id,
2797           windows->command.screen);
2798     }
2799   if (resource_info->backdrop == MagickFalse)
2800     if (windows->backdrop.mapped)
2801       {
2802         (void) XWithdrawWindow(display,windows->backdrop.id,\
2803           windows->backdrop.screen);
2804         (void) XDestroyWindow(display,windows->backdrop.id);
2805         windows->backdrop.id=(Window) NULL;
2806         (void) XWithdrawWindow(display,windows->image.id,windows->image.screen);
2807         (void) XDestroyWindow(display,windows->image.id);
2808         windows->image.id=(Window) NULL;
2809       }
2810   XSetCursorState(display,windows,MagickTrue);
2811   XCheckRefreshWindows(display,windows);
2812   for (scene=1; scene < (ssize_t) number_scenes; scene++)
2813   {
2814     if (windows->image.pixmaps[scene] != (Pixmap) NULL)
2815       (void) XFreePixmap(display,windows->image.pixmaps[scene]);
2816     windows->image.pixmaps[scene]=(Pixmap) NULL;
2817     if (windows->image.matte_pixmaps[scene] != (Pixmap) NULL)
2818       (void) XFreePixmap(display,windows->image.matte_pixmaps[scene]);
2819     windows->image.matte_pixmaps[scene]=(Pixmap) NULL;
2820   }
2821   XSetCursorState(display,windows,MagickFalse);
2822   windows->image.pixmaps=(Pixmap *)
2823     RelinquishMagickMemory(windows->image.pixmaps);
2824   windows->image.matte_pixmaps=(Pixmap *)
2825     RelinquishMagickMemory(windows->image.matte_pixmaps);
2826   if (nexus == (Image *) NULL)
2827     {
2828       /*
2829         Free X resources.
2830       */
2831       if (windows->image.mapped != MagickFalse)
2832         (void) XWithdrawWindow(display,windows->image.id,windows->image.screen);      XDelay(display,SuspendTime);
2833       (void) XFreeStandardColormap(display,icon_visual,icon_map,icon_pixel);
2834       if (resource_info->map_type == (char *) NULL)
2835         (void) XFreeStandardColormap(display,visual_info,map_info,pixel);
2836       DestroyXResources();
2837     }
2838   (void) XSync(display,MagickFalse);
2839   /*
2840     Restore our progress monitor and warning handlers.
2841   */
2842   (void) SetErrorHandler(warning_handler);
2843   (void) SetWarningHandler(warning_handler);
2844   /*
2845     Change to home directory.
2846   */
2847   directory=getcwd(working_directory,MaxTextExtent);
2848   (void) directory;
2849   status=chdir(resource_info->home_directory);
2850   if (status == -1)
2851     (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
2852       "UnableToOpenFile","%s",resource_info->home_directory);
2853   return(nexus);
2854 }
2855 \f
2856 /*
2857 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2858 %                                                                             %
2859 %                                                                             %
2860 %                                                                             %
2861 +   X S a v e I m a g e                                                       %
2862 %                                                                             %
2863 %                                                                             %
2864 %                                                                             %
2865 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2866 %
2867 %  XSaveImage() saves an image to a file.
2868 %
2869 %  The format of the XSaveImage method is:
2870 %
2871 %      MagickBooleanType XSaveImage(Display *display,
2872 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
2873 %        ExceptionInfo *exception)
2874 %
2875 %  A description of each parameter follows:
2876 %
2877 %    o status: Method XSaveImage return True if the image is
2878 %      written.  False is returned is there is a memory shortage or if the
2879 %      image fails to write.
2880 %
2881 %    o display: Specifies a connection to an X server; returned from
2882 %      XOpenDisplay.
2883 %
2884 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2885 %
2886 %    o windows: Specifies a pointer to a XWindows structure.
2887 %
2888 %    o image: the image.
2889 %
2890 */
2891 static MagickBooleanType XSaveImage(Display *display,
2892   XResourceInfo *resource_info,XWindows *windows,Image *image,
2893   ExceptionInfo *exception)
2894 {
2895   char
2896     filename[MaxTextExtent];
2897
2898   ImageInfo
2899     *image_info;
2900
2901   MagickStatusType
2902     status;
2903
2904   /*
2905     Request file name from user.
2906   */
2907   if (resource_info->write_filename != (char *) NULL)
2908     (void) CopyMagickString(filename,resource_info->write_filename,
2909       MaxTextExtent);
2910   else
2911     {
2912       char
2913         path[MaxTextExtent];
2914
2915       int
2916         status;
2917
2918       GetPathComponent(image->filename,HeadPath,path);
2919       GetPathComponent(image->filename,TailPath,filename);
2920       status=chdir(path);
2921       if (status == -1)
2922         (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
2923           "UnableToOpenFile","%s",path);
2924     }
2925   XFileBrowserWidget(display,windows,"Save",filename);
2926   if (*filename == '\0')
2927     return(MagickTrue);
2928   if (IsPathAccessible(filename) != MagickFalse)
2929     {
2930       int
2931         status;
2932
2933       /*
2934         File exists-- seek user's permission before overwriting.
2935       */
2936       status=XConfirmWidget(display,windows,"Overwrite",filename);
2937       if (status == 0)
2938         return(MagickTrue);
2939     }
2940   image_info=CloneImageInfo(resource_info->image_info);
2941   (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
2942   (void) SetImageInfo(image_info,1,exception);
2943   if ((LocaleCompare(image_info->magick,"JPEG") == 0) ||
2944       (LocaleCompare(image_info->magick,"JPG") == 0))
2945     {
2946       char
2947         quality[MaxTextExtent];
2948
2949       int
2950         status;
2951
2952       /*
2953         Request JPEG quality from user.
2954       */
2955       (void) FormatLocaleString(quality,MaxTextExtent,"%.20g",(double)
2956         image_info->quality);
2957       status=XDialogWidget(display,windows,"Save","Enter JPEG quality:",
2958         quality);
2959       if (*quality == '\0')
2960         return(MagickTrue);
2961       image->quality=StringToUnsignedLong(quality);
2962       image_info->interlace=status != MagickFalse ?  NoInterlace :
2963         PlaneInterlace;
2964     }
2965   if ((LocaleCompare(image_info->magick,"EPS") == 0) ||
2966       (LocaleCompare(image_info->magick,"PDF") == 0) ||
2967       (LocaleCompare(image_info->magick,"PS") == 0) ||
2968       (LocaleCompare(image_info->magick,"PS2") == 0))
2969     {
2970       char
2971         geometry[MaxTextExtent];
2972
2973       /*
2974         Request page geometry from user.
2975       */
2976       (void) CopyMagickString(geometry,PSPageGeometry,MaxTextExtent);
2977       if (LocaleCompare(image_info->magick,"PDF") == 0)
2978         (void) CopyMagickString(geometry,PSPageGeometry,MaxTextExtent);
2979       if (image_info->page != (char *) NULL)
2980         (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
2981       XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
2982         "Select page geometry:",geometry);
2983       if (*geometry != '\0')
2984         image_info->page=GetPageGeometry(geometry);
2985     }
2986   /*
2987     Write image.
2988   */
2989   image=GetFirstImageInList(image);
2990   status=WriteImages(image_info,image,filename,exception);
2991   if (status != MagickFalse)
2992     image->taint=MagickFalse;
2993   image_info=DestroyImageInfo(image_info);
2994   XSetCursorState(display,windows,MagickFalse);
2995   return(status != 0 ? MagickTrue : MagickFalse);
2996 }
2997 #else
2998 \f
2999 /*
3000 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3001 %                                                                             %
3002 %                                                                             %
3003 %                                                                             %
3004 +   A n i m a t e I m a g e s                                                 %
3005 %                                                                             %
3006 %                                                                             %
3007 %                                                                             %
3008 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3009 %
3010 %  AnimateImages() repeatedly displays an image sequence to any X window
3011 %  screen.  It returns a value other than 0 if successful.  Check the
3012 %  exception member of image to determine the reason for any failure.
3013 %
3014 %  The format of the AnimateImages method is:
3015 %
3016 %      MagickBooleanType AnimateImages(const ImageInfo *image_info,
3017 %        Image *images)
3018 %
3019 %  A description of each parameter follows:
3020 %
3021 %    o image_info: the image info.
3022 %
3023 %    o image: the image.
3024 %
3025 %    o exception: return any errors or warnings in this structure.
3026 %
3027 */
3028 MagickExport MagickBooleanType AnimateImages(const ImageInfo *image_info,
3029   Image *image,ExceptionInfo *exception)
3030 {
3031   assert(image_info != (const ImageInfo *) NULL);
3032   assert(image_info->signature == MagickSignature);
3033   assert(image != (Image *) NULL);
3034   assert(image->signature == MagickSignature);
3035   if (image->debug != MagickFalse)
3036     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3037   (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
3038     "DelegateLibrarySupportNotBuiltIn","`%s' (X11)",image->filename);
3039   return(MagickFalse);
3040 }
3041 #endif