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