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