Coverage Report

Created: 2026-06-30 07:12

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