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