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