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