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