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