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