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