blob: 03ce235ad1557654fa97eba185ddfade31c14e90 [file] [log] [blame]
cristy3ed852e2009-09-05 21:47:34 +00001/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3% %
4% %
5% %
6% AAA N N IIIII M M AAA TTTTT EEEEE %
7% A A NN N I MM MM A A T E %
8% AAAAA N N N I M M M AAAAA T EEE %
9% A A N NN I M M A A T E %
10% A A N N IIIII M M A A T EEEEE %
11% %
12% %
13% Methods to Interactively Animate an Image Sequence %
14% %
15% Software Design %
16% John Cristy %
17% July 1992 %
18% %
19% %
cristy1454be72011-12-19 01:52:48 +000020% Copyright 1999-2012 ImageMagick Studio LLC, a non-profit organization %
cristy3ed852e2009-09-05 21:47:34 +000021% dedicated to making software imaging solutions freely available. %
22% %
23% You may not use this file except in compliance with the License. You may %
24% obtain a copy of the License at %
25% %
26% http://www.imagemagick.org/script/license.php %
27% %
28% Unless required by applicable law or agreed to in writing, software %
29% distributed under the License is distributed on an "AS IS" BASIS, %
30% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
31% See the License for the specific language governing permissions and %
32% limitations under the License. %
33% %
34%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35%
36%
37*/
38
39/*
40 Include declarations.
41*/
cristy4c08aed2011-07-01 19:47:50 +000042#include "MagickCore/studio.h"
43#include "MagickCore/animate.h"
44#include "MagickCore/animate-private.h"
cristy228095b2012-06-21 01:42:50 +000045#include "MagickCore/attribute.h"
cristy4c08aed2011-07-01 19:47:50 +000046#include "MagickCore/client.h"
47#include "MagickCore/color.h"
48#include "MagickCore/color-private.h"
49#include "MagickCore/colorspace.h"
cristy707f0632012-02-15 23:40:22 +000050#include "MagickCore/colorspace-private.h"
cristy4c08aed2011-07-01 19:47:50 +000051#include "MagickCore/constitute.h"
52#include "MagickCore/delegate.h"
53#include "MagickCore/exception.h"
54#include "MagickCore/exception-private.h"
55#include "MagickCore/geometry.h"
56#include "MagickCore/image-private.h"
57#include "MagickCore/layer.h"
58#include "MagickCore/list.h"
59#include "MagickCore/log.h"
60#include "MagickCore/image.h"
61#include "MagickCore/memory_.h"
62#include "MagickCore/monitor.h"
63#include "MagickCore/monitor-private.h"
64#include "MagickCore/option.h"
65#include "MagickCore/pixel-accessor.h"
66#include "MagickCore/property.h"
67#include "MagickCore/resource_.h"
68#include "MagickCore/string_.h"
69#include "MagickCore/string-private.h"
70#include "MagickCore/transform.h"
71#include "MagickCore/utility.h"
cristyd1dd6e42011-09-04 01:46:08 +000072#include "MagickCore/utility-private.h"
cristy4c08aed2011-07-01 19:47:50 +000073#include "MagickCore/version.h"
74#include "MagickCore/widget.h"
cristybcbda3f2011-09-03 13:01:22 +000075#include "MagickCore/widget-private.h"
76#include "MagickCore/xwindow.h"
cristy4c08aed2011-07-01 19:47:50 +000077#include "MagickCore/xwindow-private.h"
cristy3ed852e2009-09-05 21:47:34 +000078
79#if defined(MAGICKCORE_X11_DELEGATE)
80/*
81 Animate state declarations.
82*/
83#define AutoReverseAnimationState 0x0004
84#define ForwardAnimationState 0x0008
85#define HighlightState 0x0010
86#define PlayAnimationState 0x0020
87#define RepeatAnimationState 0x0040
88#define StepAnimationState 0x0080
89
90/*
91 Static declarations.
92*/
93static const char
94 *AnimateHelp[]=
95 {
96 "BUTTONS",
97 "",
98 " Press any button to map or unmap the Command widget.",
99 "",
100 "COMMAND WIDGET",
101 " The Command widget lists a number of sub-menus and commands.",
102 " They are",
103 "",
104 " Animate",
105 " Open...",
106 " Save...",
107 " Play",
108 " Step",
109 " Repeat",
110 " Auto Reverse",
111 " Speed",
112 " Slower",
113 " Faster",
114 " Direction",
115 " Forward",
116 " Reverse",
117 " Help",
118 " Overview",
119 " Browse Documentation",
120 " About Animate",
121 " Image Info",
122 " Quit",
123 "",
124 " Menu items with a indented triangle have a sub-menu. They",
125 " are represented above as the indented items. To access a",
126 " sub-menu item, move the pointer to the appropriate menu and",
127 " press a button and drag. When you find the desired sub-menu",
128 " item, release the button and the command is executed. Move",
129 " the pointer away from the sub-menu if you decide not to",
130 " execute a particular command.",
131 "",
132 "KEYBOARD ACCELERATORS",
133 " Accelerators are one or two key presses that effect a",
134 " particular command. The keyboard accelerators that",
135 " animate(1) understands is:",
136 "",
137 " Ctl+O Press to open an image from a file.",
138 "",
139 " space Press to display the next image in the sequence.",
140 "",
141 " < Press to speed-up the display of the images. Refer to",
142 " -delay for more information.",
143 "",
144 " > Press to slow the display of the images. Refer to",
145 " -delay for more information.",
146 "",
147 " F1 Press to display helpful information about animate(1).",
148 "",
149 " Find Press to browse documentation about ImageMagick.",
150 "",
151 " ? Press to display information about the image. Press",
152 " any key or button to erase the information.",
153 "",
154 " This information is printed: image name; image size;",
155 " and the total number of unique colors in the image.",
156 "",
157 " Ctl-q Press to discard all images and exit program.",
158 (char *) NULL
159 };
160
161/*
162 Constant declarations.
163*/
164static const char
165 *PageSizes[]=
166 {
167 "Letter",
168 "Tabloid",
169 "Ledger",
170 "Legal",
171 "Statement",
172 "Executive",
173 "A3",
174 "A4",
175 "A5",
176 "B4",
177 "B5",
178 "Folio",
179 "Quarto",
180 "10x14",
181 (char *) NULL
182 };
183
184static const unsigned char
185 HighlightBitmap[8] =
186 {
187 (unsigned char) 0xaa,
188 (unsigned char) 0x55,
189 (unsigned char) 0xaa,
190 (unsigned char) 0x55,
191 (unsigned char) 0xaa,
192 (unsigned char) 0x55,
193 (unsigned char) 0xaa,
194 (unsigned char) 0x55
195 },
196 ShadowBitmap[8] =
197 {
198 (unsigned char) 0x00,
199 (unsigned char) 0x00,
200 (unsigned char) 0x00,
201 (unsigned char) 0x00,
202 (unsigned char) 0x00,
203 (unsigned char) 0x00,
204 (unsigned char) 0x00,
205 (unsigned char) 0x00
206 };
207
208/*
209 Enumeration declarations.
210*/
211typedef enum
212{
213 OpenCommand,
214 SaveCommand,
215 PlayCommand,
216 StepCommand,
217 RepeatCommand,
218 AutoReverseCommand,
219 SlowerCommand,
220 FasterCommand,
221 ForwardCommand,
222 ReverseCommand,
223 HelpCommand,
224 BrowseDocumentationCommand,
225 VersionCommand,
226 InfoCommand,
227 QuitCommand,
228 StepBackwardCommand,
229 StepForwardCommand,
230 NullCommand
231} CommandType;
232
233/*
234 Stipples.
235*/
236#define HighlightWidth 8
237#define HighlightHeight 8
238#define ShadowWidth 8
239#define ShadowHeight 8
240
241/*
242 Forward declarations.
243*/
244static Image
245 *XMagickCommand(Display *,XResourceInfo *,XWindows *,const CommandType,
cristy051718b2011-08-28 22:49:25 +0000246 Image **,MagickStatusType *,ExceptionInfo *);
cristy3ed852e2009-09-05 21:47:34 +0000247
248static MagickBooleanType
cristy051718b2011-08-28 22:49:25 +0000249 XSaveImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *);
cristy3ed852e2009-09-05 21:47:34 +0000250
251/*
252%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
253% %
254% %
255% %
256% A n i m a t e I m a g e s %
257% %
258% %
259% %
260%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
261%
262% AnimateImages() repeatedly displays an image sequence to any X window
263% screen. It returns a value other than 0 if successful. Check the
264% exception member of image to determine the reason for any failure.
265%
266% The format of the AnimateImages method is:
267%
268% MagickBooleanType AnimateImages(const ImageInfo *image_info,
cristy051718b2011-08-28 22:49:25 +0000269% Image *images,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000270%
271% A description of each parameter follows:
272%
273% o image_info: the image info.
274%
275% o image: the image.
276%
cristy051718b2011-08-28 22:49:25 +0000277% o exception: return any errors or warnings in this structure.
278%
cristy3ed852e2009-09-05 21:47:34 +0000279*/
280MagickExport MagickBooleanType AnimateImages(const ImageInfo *image_info,
cristy051718b2011-08-28 22:49:25 +0000281 Image *images,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000282{
283 char
284 *argv[1];
285
286 Display
287 *display;
288
289 MagickStatusType
290 status;
291
292 XrmDatabase
293 resource_database;
294
295 XResourceInfo
296 resource_info;
297
298 assert(image_info != (const ImageInfo *) NULL);
299 assert(image_info->signature == MagickSignature);
300 assert(images != (Image *) NULL);
301 assert(images->signature == MagickSignature);
302 if (images->debug != MagickFalse)
303 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
304 display=XOpenDisplay(image_info->server_name);
305 if (display == (Display *) NULL)
306 {
cristy051718b2011-08-28 22:49:25 +0000307 (void) ThrowMagickException(exception,GetMagickModule(),XServerError,
anthonye5b39652012-04-21 05:37:29 +0000308 "UnableToOpenXServer","'%s'",XDisplayName(image_info->server_name));
cristy3ed852e2009-09-05 21:47:34 +0000309 return(MagickFalse);
310 }
cristy051718b2011-08-28 22:49:25 +0000311 if (exception->severity != UndefinedException)
312 CatchException(exception);
cristy3ed852e2009-09-05 21:47:34 +0000313 (void) XSetErrorHandler(XError);
314 resource_database=XGetResourceDatabase(display,GetClientName());
315 (void) ResetMagickMemory(&resource_info,0,sizeof(XResourceInfo));
316 XGetResourceInfo(image_info,resource_database,GetClientName(),&resource_info);
317 if (image_info->page != (char *) NULL)
318 resource_info.image_geometry=AcquireString(image_info->page);
319 resource_info.immutable=MagickTrue;
320 argv[0]=AcquireString(GetClientName());
cristy051718b2011-08-28 22:49:25 +0000321 (void) XAnimateImages(display,&resource_info,argv,1,images,exception);
cristye42f6582012-02-11 17:59:50 +0000322 (void) SetErrorHandler((ErrorHandler) NULL);
323 (void) SetWarningHandler((WarningHandler) NULL);
cristy3ed852e2009-09-05 21:47:34 +0000324 argv[0]=DestroyString(argv[0]);
325 (void) XCloseDisplay(display);
326 XDestroyResourceInfo(&resource_info);
cristy051718b2011-08-28 22:49:25 +0000327 status=exception->severity == UndefinedException ? MagickTrue : MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +0000328 return(status != 0 ? MagickTrue : MagickFalse);
329}
330
331/*
332%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
333% %
334% %
335% %
336+ X M a g i c k C o m m a n d %
337% %
338% %
339% %
340%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
341%
342% XMagickCommand() makes a transform to the image or Image window as specified
343% by a user menu button or keyboard command.
344%
345% The format of the XMagickCommand method is:
346%
347% Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
348% XWindows *windows,const CommandType command_type,Image **image,
cristy051718b2011-08-28 22:49:25 +0000349% MagickStatusType *state,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000350%
351% A description of each parameter follows:
352%
353% o display: Specifies a connection to an X server; returned from
354% XOpenDisplay.
355%
356% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
357%
358% o windows: Specifies a pointer to a XWindows structure.
359%
360% o image: the image; XMagickCommand
361% may transform the image and return a new image pointer.
362%
363% o state: Specifies a MagickStatusType; XMagickCommand may return a
364% modified state.
365%
cristy051718b2011-08-28 22:49:25 +0000366% o exception: return any errors or warnings in this structure.
367%
cristy3ed852e2009-09-05 21:47:34 +0000368%
369*/
370static Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
371 XWindows *windows,const CommandType command_type,Image **image,
cristy051718b2011-08-28 22:49:25 +0000372 MagickStatusType *state,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000373{
374 Image
375 *nexus;
376
377 MagickBooleanType
378 proceed;
379
380 MagickStatusType
381 status;
382
383 XTextProperty
384 window_name;
385
386 /*
387 Process user command.
388 */
389 nexus=NewImageList();
390 switch (command_type)
391 {
392 case OpenCommand:
393 {
394 char
395 **filelist;
396
cristy3ed852e2009-09-05 21:47:34 +0000397 Image
398 *images,
399 *next;
400
401 ImageInfo
402 *read_info;
403
404 int
405 number_files;
406
407 register int
408 i;
409
410 static char
411 filenames[MaxTextExtent] = "*";
412
413 if (resource_info->immutable != MagickFalse)
414 break;
415 /*
416 Request file name from user.
417 */
418 XFileBrowserWidget(display,windows,"Animate",filenames);
419 if (*filenames == '\0')
420 return((Image *) NULL);
421 /*
422 Expand the filenames.
423 */
cristy73bd4a52010-10-05 11:24:23 +0000424 filelist=(char **) AcquireMagickMemory(sizeof(char *));
cristy3ed852e2009-09-05 21:47:34 +0000425 if (filelist == (char **) NULL)
426 {
427 ThrowXWindowException(ResourceLimitError,"MemoryAllocationFailed",
428 filenames);
429 return((Image *) NULL);
430 }
431 number_files=1;
432 filelist[0]=filenames;
433 status=ExpandFilenames(&number_files,&filelist);
434 if ((status == MagickFalse) || (number_files == 0))
435 {
436 if (number_files == 0)
437 {
438 ThrowXWindowException(ImageError,"NoImagesWereLoaded",filenames);
439 return((Image *) NULL);
440 }
441 ThrowXWindowException(ResourceLimitError,"MemoryAllocationFailed",
442 filenames);
443 return((Image *) NULL);
444 }
445 read_info=CloneImageInfo(resource_info->image_info);
cristy3ed852e2009-09-05 21:47:34 +0000446 images=NewImageList();
447 XSetCursorState(display,windows,MagickTrue);
448 XCheckRefreshWindows(display,windows);
449 for (i=0; i < number_files; i++)
450 {
451 (void) CopyMagickString(read_info->filename,filelist[i],MaxTextExtent);
452 filelist[i]=DestroyString(filelist[i]);
453 *read_info->magick='\0';
454 next=ReadImage(read_info,exception);
455 CatchException(exception);
456 if (next != (Image *) NULL)
457 AppendImageToList(&images,next);
458 if (number_files <= 5)
459 continue;
460 proceed=SetImageProgress(images,LoadImageTag,i,(MagickSizeType)
461 number_files);
462 if (proceed == MagickFalse)
463 break;
464 }
465 filelist=(char **) RelinquishMagickMemory(filelist);
cristy3ed852e2009-09-05 21:47:34 +0000466 read_info=DestroyImageInfo(read_info);
467 if (images == (Image *) NULL)
468 {
469 XSetCursorState(display,windows,MagickFalse);
470 ThrowXWindowException(ImageError,"NoImagesWereLoaded",filenames);
471 return((Image *) NULL);
472 }
473 nexus=GetFirstImageInList(images);
474 *state|=ExitState;
475 break;
476 }
477 case PlayCommand:
478 {
479 char
480 basename[MaxTextExtent];
481
482 int
483 status;
484
485 /*
486 Window name is the base of the filename.
487 */
488 *state|=PlayAnimationState;
489 *state&=(~AutoReverseAnimationState);
490 GetPathComponent((*image)->magick_filename,BasePath,basename);
cristyb51dff52011-05-19 16:55:47 +0000491 (void) FormatLocaleString(windows->image.name,MaxTextExtent,
cristy40a08ad2010-02-09 02:27:44 +0000492 "%s: %s",MagickPackageName,basename);
cristy3ed852e2009-09-05 21:47:34 +0000493 if (resource_info->title != (char *) NULL)
494 {
495 char
496 *title;
497
498 title=InterpretImageProperties(resource_info->image_info,*image,
cristy018f07f2011-09-04 21:15:19 +0000499 resource_info->title,exception);
cristy3ed852e2009-09-05 21:47:34 +0000500 (void) CopyMagickString(windows->image.name,title,MaxTextExtent);
501 title=DestroyString(title);
502 }
503 status=XStringListToTextProperty(&windows->image.name,1,&window_name);
504 if (status == 0)
505 break;
506 XSetWMName(display,windows->image.id,&window_name);
507 (void) XFree((void *) window_name.value);
508 break;
509 }
510 case StepCommand:
511 case StepBackwardCommand:
512 case StepForwardCommand:
513 {
514 *state|=StepAnimationState;
515 *state&=(~PlayAnimationState);
516 if (command_type == StepBackwardCommand)
517 *state&=(~ForwardAnimationState);
518 if (command_type == StepForwardCommand)
519 *state|=ForwardAnimationState;
520 if (resource_info->title != (char *) NULL)
521 break;
522 break;
523 }
524 case RepeatCommand:
525 {
526 *state|=RepeatAnimationState;
527 *state&=(~AutoReverseAnimationState);
528 *state|=PlayAnimationState;
529 break;
530 }
531 case AutoReverseCommand:
532 {
533 *state|=AutoReverseAnimationState;
534 *state&=(~RepeatAnimationState);
535 *state|=PlayAnimationState;
536 break;
537 }
538 case SaveCommand:
539 {
540 /*
541 Save image.
542 */
cristy051718b2011-08-28 22:49:25 +0000543 status=XSaveImage(display,resource_info,windows,*image,exception);
cristy3ed852e2009-09-05 21:47:34 +0000544 if (status == MagickFalse)
545 {
cristyc663dbd2011-09-16 19:43:14 +0000546 char
547 message[MaxTextExtent];
548
549 (void) FormatLocaleString(message,MaxTextExtent,"%s:%s",
550 exception->reason != (char *) NULL ? exception->reason : "",
551 exception->description != (char *) NULL ? exception->description :
552 "");
553 XNoticeWidget(display,windows,"Unable to save file:",message);
cristy3ed852e2009-09-05 21:47:34 +0000554 break;
555 }
556 break;
557 }
558 case SlowerCommand:
559 {
560 resource_info->delay++;
561 break;
562 }
563 case FasterCommand:
564 {
565 if (resource_info->delay == 0)
566 break;
567 resource_info->delay--;
568 break;
569 }
570 case ForwardCommand:
571 {
572 *state=ForwardAnimationState;
573 *state&=(~AutoReverseAnimationState);
574 break;
575 }
576 case ReverseCommand:
577 {
578 *state&=(~ForwardAnimationState);
579 *state&=(~AutoReverseAnimationState);
580 break;
581 }
582 case InfoCommand:
583 {
cristy6710d842011-10-20 23:23:00 +0000584 XDisplayImageInfo(display,resource_info,windows,(Image *) NULL,*image,
585 exception);
cristy3ed852e2009-09-05 21:47:34 +0000586 break;
587 }
588 case HelpCommand:
589 {
590 /*
591 User requested help.
592 */
593 XTextViewWidget(display,resource_info,windows,MagickFalse,
594 "Help Viewer - Animate",AnimateHelp);
595 break;
596 }
597 case BrowseDocumentationCommand:
598 {
599 Atom
600 mozilla_atom;
601
602 Window
603 mozilla_window,
604 root_window;
605
606 /*
607 Browse the ImageMagick documentation.
608 */
609 root_window=XRootWindow(display,XDefaultScreen(display));
610 mozilla_atom=XInternAtom(display,"_MOZILLA_VERSION",MagickFalse);
611 mozilla_window=XWindowByProperty(display,root_window,mozilla_atom);
612 if (mozilla_window != (Window) NULL)
613 {
614 char
615 command[MaxTextExtent],
616 *url;
617
618 /*
619 Display documentation using Netscape remote control.
620 */
621 url=GetMagickHomeURL();
cristyb51dff52011-05-19 16:55:47 +0000622 (void) FormatLocaleString(command,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +0000623 "openurl(%s,new-tab)",url);
624 url=DestroyString(url);
625 mozilla_atom=XInternAtom(display,"_MOZILLA_COMMAND",MagickFalse);
626 (void) XChangeProperty(display,mozilla_window,mozilla_atom,
627 XA_STRING,8,PropModeReplace,(unsigned char *) command,
628 (int) strlen(command));
629 XSetCursorState(display,windows,MagickFalse);
630 break;
631 }
632 XSetCursorState(display,windows,MagickTrue);
633 XCheckRefreshWindows(display,windows);
634 status=InvokeDelegate(resource_info->image_info,*image,"browse",
cristy051718b2011-08-28 22:49:25 +0000635 (char *) NULL,exception);
cristy3ed852e2009-09-05 21:47:34 +0000636 if (status == MagickFalse)
637 XNoticeWidget(display,windows,"Unable to browse documentation",
638 (char *) NULL);
639 XDelay(display,1500);
640 XSetCursorState(display,windows,MagickFalse);
641 break;
642 }
643 case VersionCommand:
644 {
cristybb503372010-05-27 20:51:26 +0000645 XNoticeWidget(display,windows,GetMagickVersion((size_t *) NULL),
cristy3ed852e2009-09-05 21:47:34 +0000646 GetMagickCopyright());
647 break;
648 }
649 case QuitCommand:
650 {
651 /*
652 exit program
653 */
654 if (resource_info->confirm_exit == MagickFalse)
655 XClientMessage(display,windows->image.id,windows->im_protocols,
656 windows->im_exit,CurrentTime);
657 else
658 {
659 int
660 status;
661
662 /*
663 Confirm program exit.
664 */
665 status=XConfirmWidget(display,windows,"Do you really want to exit",
666 resource_info->client_name);
667 if (status != 0)
668 XClientMessage(display,windows->image.id,windows->im_protocols,
669 windows->im_exit,CurrentTime);
670 }
671 break;
672 }
673 default:
674 break;
675 }
676 return(nexus);
677}
678
679/*
680%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
681% %
682% %
683% %
684+ X A n i m a t e B a c k g r o u n d I m a g e %
685% %
686% %
687% %
688%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
689%
690% XAnimateBackgroundImage() animates an image sequence in the background of
691% a window.
692%
693% The format of the XAnimateBackgroundImage method is:
694%
695% void XAnimateBackgroundImage(Display *display,
cristy051718b2011-08-28 22:49:25 +0000696% XResourceInfo *resource_info,Image *images,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000697%
698% A description of each parameter follows:
699%
700% o display: Specifies a connection to an X server; returned from
701% XOpenDisplay.
702%
703% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
704%
705% o images: the image list.
706%
cristy051718b2011-08-28 22:49:25 +0000707% o exception: return any errors or warnings in this structure.
708%
cristy3ed852e2009-09-05 21:47:34 +0000709*/
710
cristybb503372010-05-27 20:51:26 +0000711static inline ssize_t MagickMax(const ssize_t x,const ssize_t y)
cristy3ed852e2009-09-05 21:47:34 +0000712{
713 if (x > y)
714 return(x);
715 return(y);
716}
717
718#if defined(__cplusplus) || defined(c_plusplus)
719extern "C" {
720#endif
721
722static int SceneCompare(const void *x,const void *y)
723{
724 const Image
725 **image_1,
726 **image_2;
727
728 image_1=(const Image **) x;
729 image_2=(const Image **) y;
730 return((int) ((*image_1)->scene-(*image_2)->scene));
731}
732
733#if defined(__cplusplus) || defined(c_plusplus)
734}
735#endif
736
737MagickExport void XAnimateBackgroundImage(Display *display,
cristy051718b2011-08-28 22:49:25 +0000738 XResourceInfo *resource_info,Image *images,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000739{
740 char
741 geometry[MaxTextExtent],
742 visual_type[MaxTextExtent];
743
744 Image
745 *coalesce_image,
746 *display_image,
747 **image_list;
748
749 int
750 scene;
751
752 MagickStatusType
753 status;
754
755 RectangleInfo
756 geometry_info;
757
cristybb503372010-05-27 20:51:26 +0000758 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000759 i;
760
761 size_t
762 number_scenes;
763
764 static XPixelInfo
765 pixel;
766
767 static XStandardColormap
768 *map_info;
769
770 static XVisualInfo
771 *visual_info = (XVisualInfo *) NULL;
772
773 static XWindowInfo
774 window_info;
775
776 unsigned int
777 height,
778 width;
779
cristybb503372010-05-27 20:51:26 +0000780 size_t
cristy3ed852e2009-09-05 21:47:34 +0000781 delay;
782
783 Window
784 root_window;
785
786 XEvent
787 event;
788
789 XGCValues
790 context_values;
791
792 XResourceInfo
793 resources;
794
795 XWindowAttributes
796 window_attributes;
797
798 /*
799 Determine target window.
800 */
801 assert(images != (Image *) NULL);
802 assert(images->signature == MagickSignature);
803 if (images->debug != MagickFalse)
804 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
805 resources=(*resource_info);
806 window_info.id=(Window) NULL;
807 root_window=XRootWindow(display,XDefaultScreen(display));
808 if (LocaleCompare(resources.window_id,"root") == 0)
809 window_info.id=root_window;
810 else
811 {
812 if (isdigit((int) ((unsigned char) *resources.window_id)) != 0)
813 window_info.id=XWindowByID(display,root_window,
814 (Window) strtol((char *) resources.window_id,(char **) NULL,0));
815 if (window_info.id == (Window) NULL)
816 window_info.id=
817 XWindowByName(display,root_window,resources.window_id);
818 }
819 if (window_info.id == (Window) NULL)
820 {
821 ThrowXWindowException(XServerError,"NoWindowWithSpecifiedIDExists",
822 resources.window_id);
823 return;
824 }
825 /*
826 Determine window visual id.
827 */
828 window_attributes.width=XDisplayWidth(display,XDefaultScreen(display));
829 window_attributes.height=XDisplayHeight(display,XDefaultScreen(display));
830 (void) CopyMagickString(visual_type,"default",MaxTextExtent);
831 status=XGetWindowAttributes(display,window_info.id,&window_attributes) != 0 ?
832 MagickTrue : MagickFalse;
833 if (status != MagickFalse)
cristyb51dff52011-05-19 16:55:47 +0000834 (void) FormatLocaleString(visual_type,MaxTextExtent,"0x%lx",
cristy3ed852e2009-09-05 21:47:34 +0000835 XVisualIDFromVisual(window_attributes.visual));
836 if (visual_info == (XVisualInfo *) NULL)
837 {
838 /*
839 Allocate standard colormap.
840 */
841 map_info=XAllocStandardColormap();
842 if (map_info == (XStandardColormap *) NULL)
843 ThrowXWindowFatalException(ResourceLimitError,"MemoryAllocationFailed",
844 images->filename);
845 map_info->colormap=(Colormap) NULL;
cristyf2faecf2010-05-28 19:19:36 +0000846 pixel.pixels=(unsigned long *) NULL;
cristy3ed852e2009-09-05 21:47:34 +0000847 /*
848 Initialize visual info.
849 */
850 resources.map_type=(char *) NULL;
851 resources.visual_type=visual_type;
852 visual_info=XBestVisualInfo(display,map_info,&resources);
853 if (visual_info == (XVisualInfo *) NULL)
854 ThrowXWindowFatalException(XServerFatalError,"UnableToGetVisual",
855 images->filename);
856 /*
857 Initialize window info.
858 */
859 window_info.ximage=(XImage *) NULL;
860 window_info.matte_image=(XImage *) NULL;
861 window_info.pixmap=(Pixmap) NULL;
862 window_info.matte_pixmap=(Pixmap) NULL;
863 }
864 /*
865 Free previous root colors.
866 */
867 if (window_info.id == root_window)
868 XDestroyWindowColors(display,root_window);
cristy051718b2011-08-28 22:49:25 +0000869 coalesce_image=CoalesceImages(images,exception);
cristy3ed852e2009-09-05 21:47:34 +0000870 if (coalesce_image == (Image *) NULL)
871 ThrowXWindowFatalException(ResourceLimitError,"MemoryAllocationFailed",
872 images->filename);
873 images=coalesce_image;
874 if (resources.map_type == (char *) NULL)
875 if ((visual_info->klass != TrueColor) &&
876 (visual_info->klass != DirectColor))
877 {
878 Image
879 *next;
880
881 /*
882 Determine if the sequence of images has the identical colormap.
883 */
884 for (next=images; next != (Image *) NULL; )
885 {
cristy8a46d822012-08-28 23:32:39 +0000886 next->alpha_trait=UndefinedPixelTrait;
cristy3ed852e2009-09-05 21:47:34 +0000887 if ((next->storage_class == DirectClass) ||
888 (next->colors != images->colors) ||
cristybb503372010-05-27 20:51:26 +0000889 (next->colors > (size_t) visual_info->colormap_size))
cristy3ed852e2009-09-05 21:47:34 +0000890 break;
cristybb503372010-05-27 20:51:26 +0000891 for (i=0; i < (ssize_t) images->colors; i++)
cristy101ab702011-10-13 13:06:32 +0000892 if (IsPixelInfoEquivalent(next->colormap+i,images->colormap+i) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +0000893 break;
cristybb503372010-05-27 20:51:26 +0000894 if (i < (ssize_t) images->colors)
cristy3ed852e2009-09-05 21:47:34 +0000895 break;
896 next=GetNextImageInList(next);
897 }
898 if (next != (Image *) NULL)
cristy018f07f2011-09-04 21:15:19 +0000899 (void) RemapImages(resources.quantize_info,images,(Image *) NULL,
900 exception);
cristy3ed852e2009-09-05 21:47:34 +0000901 }
902 /*
903 Sort images by increasing scene number.
904 */
905 number_scenes=GetImageListLength(images);
cristy051718b2011-08-28 22:49:25 +0000906 image_list=ImageListToArray(images,exception);
cristy3ed852e2009-09-05 21:47:34 +0000907 if (image_list == (Image **) NULL)
908 ThrowXWindowFatalException(ResourceLimitFatalError,
909 "MemoryAllocationFailed",images->filename);
cristybb503372010-05-27 20:51:26 +0000910 for (i=0; i < (ssize_t) number_scenes; i++)
cristy3ed852e2009-09-05 21:47:34 +0000911 if (image_list[i]->scene == 0)
912 break;
cristybb503372010-05-27 20:51:26 +0000913 if (i == (ssize_t) number_scenes)
cristy3ed852e2009-09-05 21:47:34 +0000914 qsort((void *) image_list,number_scenes,sizeof(Image *),SceneCompare);
915 /*
916 Initialize Standard Colormap.
917 */
918 resources.colormap=SharedColormap;
919 display_image=image_list[0];
920 for (scene=0; scene < (int) number_scenes; scene++)
921 {
922 if ((resource_info->map_type != (char *) NULL) ||
923 (visual_info->klass == TrueColor) ||
924 (visual_info->klass == DirectColor))
cristy8a46d822012-08-28 23:32:39 +0000925 (void) SetImageType(image_list[scene],image_list[scene]->alpha_trait ==
926 BlendPixelTrait ? TrueColorType : TrueColorMatteType,exception);
cristy3ed852e2009-09-05 21:47:34 +0000927 if ((display_image->columns < image_list[scene]->columns) &&
928 (display_image->rows < image_list[scene]->rows))
929 display_image=image_list[scene];
930 }
931 if ((resource_info->map_type != (char *) NULL) ||
932 (visual_info->klass == TrueColor) || (visual_info->klass == DirectColor))
cristy8a46d822012-08-28 23:32:39 +0000933 (void) SetImageType(display_image,display_image->alpha_trait !=
934 BlendPixelTrait ? TrueColorType : TrueColorMatteType,exception);
cristy3ed852e2009-09-05 21:47:34 +0000935 XMakeStandardColormap(display,visual_info,&resources,display_image,map_info,
cristy6710d842011-10-20 23:23:00 +0000936 &pixel,exception);
cristy3ed852e2009-09-05 21:47:34 +0000937 /*
938 Graphic context superclass.
939 */
940 context_values.background=pixel.background_color.pixel;
941 context_values.foreground=pixel.foreground_color.pixel;
cristycee97112010-05-28 00:44:52 +0000942 pixel.annotate_context=XCreateGC(display,window_info.id,(unsigned long)
943 (GCBackground | GCForeground),&context_values);
cristy3ed852e2009-09-05 21:47:34 +0000944 if (pixel.annotate_context == (GC) NULL)
945 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
946 images->filename);
947 /*
948 Initialize Image window attributes.
949 */
950 XGetWindowInfo(display,visual_info,map_info,&pixel,(XFontStruct *) NULL,
951 &resources,&window_info);
952 /*
953 Create the X image.
954 */
955 window_info.width=(unsigned int) image_list[0]->columns;
956 window_info.height=(unsigned int) image_list[0]->rows;
957 if ((image_list[0]->columns != window_info.width) ||
958 (image_list[0]->rows != window_info.height))
959 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
960 image_list[0]->filename);
cristyb51dff52011-05-19 16:55:47 +0000961 (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>",
cristy3ed852e2009-09-05 21:47:34 +0000962 window_attributes.width,window_attributes.height);
963 geometry_info.width=window_info.width;
964 geometry_info.height=window_info.height;
cristyecd0ab52010-05-30 14:59:20 +0000965 geometry_info.x=(ssize_t) window_info.x;
966 geometry_info.y=(ssize_t) window_info.y;
cristy3ed852e2009-09-05 21:47:34 +0000967 (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
968 &geometry_info.width,&geometry_info.height);
969 window_info.width=(unsigned int) geometry_info.width;
970 window_info.height=(unsigned int) geometry_info.height;
971 window_info.x=(int) geometry_info.x;
972 window_info.y=(int) geometry_info.y;
973 status=XMakeImage(display,&resources,&window_info,image_list[0],
cristy051718b2011-08-28 22:49:25 +0000974 window_info.width,window_info.height,exception);
cristy3ed852e2009-09-05 21:47:34 +0000975 if (status == MagickFalse)
976 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
977 images->filename);
978 window_info.x=0;
979 window_info.y=0;
980 if (display_image->debug != MagickFalse)
981 {
982 (void) LogMagickEvent(X11Event,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +0000983 "Image: %s[%.20g] %.20gx%.20g ",image_list[0]->filename,(double)
984 image_list[0]->scene,(double) image_list[0]->columns,(double)
985 image_list[0]->rows);
cristy3ed852e2009-09-05 21:47:34 +0000986 if (image_list[0]->colors != 0)
cristye8c25f92010-06-03 00:53:06 +0000987 (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
988 image_list[0]->colors);
cristy3ed852e2009-09-05 21:47:34 +0000989 (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",
990 image_list[0]->magick);
991 }
992 /*
993 Adjust image dimensions as specified by backdrop or geometry options.
994 */
995 width=window_info.width;
996 height=window_info.height;
997 if (resources.backdrop != MagickFalse)
998 {
999 /*
1000 Center image on window.
1001 */
1002 window_info.x=(int) (window_attributes.width/2)-
1003 (window_info.ximage->width/2);
1004 window_info.y=(int) (window_attributes.height/2)-
1005 (window_info.ximage->height/2);
1006 width=(unsigned int) window_attributes.width;
1007 height=(unsigned int) window_attributes.height;
1008 }
1009 if (resources.image_geometry != (char *) NULL)
1010 {
1011 char
1012 default_geometry[MaxTextExtent];
1013
1014 int
1015 flags,
1016 gravity;
1017
1018 XSizeHints
1019 *size_hints;
1020
1021 /*
1022 User specified geometry.
1023 */
1024 size_hints=XAllocSizeHints();
1025 if (size_hints == (XSizeHints *) NULL)
1026 ThrowXWindowFatalException(ResourceLimitFatalError,
1027 "MemoryAllocationFailed",images->filename);
1028 size_hints->flags=0L;
cristyb51dff52011-05-19 16:55:47 +00001029 (void) FormatLocaleString(default_geometry,MaxTextExtent,"%ux%u",width,
cristy3ed852e2009-09-05 21:47:34 +00001030 height);
1031 flags=XWMGeometry(display,visual_info->screen,resources.image_geometry,
1032 default_geometry,window_info.border_width,size_hints,&window_info.x,
1033 &window_info.y,(int *) &width,(int *) &height,&gravity);
1034 if (((flags & (XValue | YValue))) != 0)
1035 {
1036 width=(unsigned int) window_attributes.width;
1037 height=(unsigned int) window_attributes.height;
1038 }
1039 (void) XFree((void *) size_hints);
1040 }
1041 /*
1042 Create the X pixmap.
1043 */
1044 window_info.pixmap=XCreatePixmap(display,window_info.id,(unsigned int) width,
1045 (unsigned int) height,window_info.depth);
1046 if (window_info.pixmap == (Pixmap) NULL)
1047 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXPixmap",
1048 images->filename);
1049 /*
1050 Display pixmap on the window.
1051 */
1052 if (((unsigned int) width > window_info.width) ||
1053 ((unsigned int) height > window_info.height))
1054 (void) XFillRectangle(display,window_info.pixmap,
1055 window_info.annotate_context,0,0,(unsigned int) width,
1056 (unsigned int) height);
1057 (void) XPutImage(display,window_info.pixmap,window_info.annotate_context,
1058 window_info.ximage,0,0,window_info.x,window_info.y,window_info.width,
1059 window_info.height);
1060 (void) XSetWindowBackgroundPixmap(display,window_info.id,window_info.pixmap);
1061 (void) XClearWindow(display,window_info.id);
1062 /*
1063 Initialize image pixmaps structure.
1064 */
1065 window_info.pixmaps=(Pixmap *) AcquireQuantumMemory(number_scenes,
1066 sizeof(*window_info.pixmaps));
1067 window_info.matte_pixmaps=(Pixmap *) AcquireQuantumMemory(number_scenes,
1068 sizeof(*window_info.matte_pixmaps));
1069 if ((window_info.pixmaps == (Pixmap *) NULL) ||
1070 (window_info.matte_pixmaps == (Pixmap *) NULL))
1071 ThrowXWindowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed",
1072 images->filename);
1073 window_info.pixmaps[0]=window_info.pixmap;
1074 window_info.matte_pixmaps[0]=window_info.pixmap;
1075 for (scene=1; scene < (int) number_scenes; scene++)
1076 {
1077 unsigned int
1078 columns,
1079 rows;
1080
1081 /*
1082 Create X image.
1083 */
1084 window_info.pixmap=(Pixmap) NULL;
1085 window_info.matte_pixmap=(Pixmap) NULL;
1086 if ((resources.map_type != (char *) NULL) ||
1087 (visual_info->klass == TrueColor) ||
1088 (visual_info->klass == DirectColor))
1089 if (image_list[scene]->storage_class == PseudoClass)
cristy4c08aed2011-07-01 19:47:50 +00001090 XGetPixelInfo(display,visual_info,map_info,&resources,
cristy3ed852e2009-09-05 21:47:34 +00001091 image_list[scene],window_info.pixel_info);
1092 columns=(unsigned int) image_list[scene]->columns;
1093 rows=(unsigned int) image_list[scene]->rows;
1094 if ((image_list[scene]->columns != columns) ||
1095 (image_list[scene]->rows != rows))
1096 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
1097 image_list[scene]->filename);
1098 status=XMakeImage(display,&resources,&window_info,image_list[scene],
cristy051718b2011-08-28 22:49:25 +00001099 columns,rows,exception);
cristy3ed852e2009-09-05 21:47:34 +00001100 if (status == MagickFalse)
1101 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
1102 images->filename);
1103 if (display_image->debug != MagickFalse)
1104 {
1105 (void) LogMagickEvent(X11Event,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00001106 "Image: [%.20g] %s %.20gx%.20g ",(double) image_list[scene]->scene,
1107 image_list[scene]->filename,(double) columns,(double) rows);
cristy3ed852e2009-09-05 21:47:34 +00001108 if (image_list[scene]->colors != 0)
cristye8c25f92010-06-03 00:53:06 +00001109 (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
1110 image_list[scene]->colors);
cristy3ed852e2009-09-05 21:47:34 +00001111 (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",
1112 image_list[scene]->magick);
1113 }
1114 /*
1115 Create the X pixmap.
1116 */
1117 window_info.pixmap=XCreatePixmap(display,window_info.id,width,height,
1118 window_info.depth);
1119 if (window_info.pixmap == (Pixmap) NULL)
1120 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXPixmap",
1121 images->filename);
1122 /*
1123 Display pixmap on the window.
1124 */
1125 if ((width > window_info.width) || (height > window_info.height))
1126 (void) XFillRectangle(display,window_info.pixmap,
1127 window_info.annotate_context,0,0,width,height);
1128 (void) XPutImage(display,window_info.pixmap,window_info.annotate_context,
1129 window_info.ximage,0,0,window_info.x,window_info.y,window_info.width,
1130 window_info.height);
1131 (void) XSetWindowBackgroundPixmap(display,window_info.id,
1132 window_info.pixmap);
1133 (void) XClearWindow(display,window_info.id);
1134 window_info.pixmaps[scene]=window_info.pixmap;
1135 window_info.matte_pixmaps[scene]=window_info.matte_pixmap;
cristy8a46d822012-08-28 23:32:39 +00001136 if (image_list[scene]->alpha_trait)
cristy3ed852e2009-09-05 21:47:34 +00001137 (void) XClearWindow(display,window_info.id);
1138 delay=1000*image_list[scene]->delay/MagickMax(
1139 image_list[scene]->ticks_per_second,1L);
1140 XDelay(display,resources.delay*(delay == 0 ? 10 : delay));
1141 }
1142 window_info.pixel_info=(&pixel);
1143 /*
1144 Display pixmap on the window.
1145 */
1146 (void) XSelectInput(display,window_info.id,SubstructureNotifyMask);
1147 event.type=Expose;
1148 do
1149 {
1150 for (scene=0; scene < (int) number_scenes; scene++)
1151 {
1152 if (XEventsQueued(display,QueuedAfterFlush) > 0)
1153 {
1154 (void) XNextEvent(display,&event);
1155 if (event.type == DestroyNotify)
1156 break;
1157 }
1158 window_info.pixmap=window_info.pixmaps[scene];
1159 window_info.matte_pixmap=window_info.matte_pixmaps[scene];
1160 (void) XSetWindowBackgroundPixmap(display,window_info.id,
1161 window_info.pixmap);
1162 (void) XClearWindow(display,window_info.id);
1163 (void) XSync(display,MagickFalse);
1164 delay=1000*image_list[scene]->delay/MagickMax(
1165 image_list[scene]->ticks_per_second,1L);
1166 XDelay(display,resources.delay*(delay == 0 ? 10 : delay));
1167 }
1168 } while (event.type != DestroyNotify);
1169 (void) XSync(display,MagickFalse);
1170 image_list=(Image **) RelinquishMagickMemory(image_list);
1171 images=DestroyImageList(images);
1172}
1173
1174/*
1175%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1176% %
1177% %
1178% %
1179+ X A n i m a t e I m a g e s %
1180% %
1181% %
1182% %
1183%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1184%
1185% XAnimateImages() displays an image via X11.
1186%
1187% The format of the XAnimateImages method is:
1188%
1189% Image *XAnimateImages(Display *display,XResourceInfo *resource_info,
cristy051718b2011-08-28 22:49:25 +00001190% char **argv,const int argc,Image *images,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001191%
1192% A description of each parameter follows:
1193%
1194% o display: Specifies a connection to an X server; returned from
1195% XOpenDisplay.
1196%
1197% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
1198%
1199% o argv: Specifies the application's argument list.
1200%
1201% o argc: Specifies the number of arguments.
1202%
1203% o images: the image list.
1204%
cristy051718b2011-08-28 22:49:25 +00001205% o exception: return any errors or warnings in this structure.
1206%
cristy3ed852e2009-09-05 21:47:34 +00001207*/
1208MagickExport Image *XAnimateImages(Display *display,
cristy051718b2011-08-28 22:49:25 +00001209 XResourceInfo *resource_info,char **argv,const int argc,Image *images,
1210 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001211{
1212#define MagickMenus 4
1213#define MaXWindows 8
1214#define MagickTitle "Commands"
1215
1216 static const char
1217 *CommandMenu[]=
1218 {
1219 "Animate",
1220 "Speed",
1221 "Direction",
1222 "Help",
1223 "Image Info",
1224 "Quit",
1225 (char *) NULL
1226 },
1227 *AnimateMenu[]=
1228 {
1229 "Open...",
1230 "Play",
1231 "Step",
1232 "Repeat",
1233 "Auto Reverse",
1234 "Save...",
1235 (char *) NULL
1236 },
1237 *SpeedMenu[]=
1238 {
1239 "Faster",
1240 "Slower",
1241 (char *) NULL
1242 },
1243 *DirectionMenu[]=
1244 {
1245 "Forward",
1246 "Reverse",
1247 (char *) NULL
1248 },
1249 *HelpMenu[]=
1250 {
1251 "Overview",
1252 "Browse Documentation",
1253 "About Animate",
1254 (char *) NULL
1255 };
1256
1257 static const char
1258 **Menus[MagickMenus]=
1259 {
1260 AnimateMenu,
1261 SpeedMenu,
1262 DirectionMenu,
1263 HelpMenu
1264 };
1265
1266 static const CommandType
1267 CommandMenus[]=
1268 {
1269 NullCommand,
1270 NullCommand,
1271 NullCommand,
1272 NullCommand,
1273 InfoCommand,
1274 QuitCommand
1275 },
1276 CommandTypes[]=
1277 {
1278 OpenCommand,
1279 PlayCommand,
1280 StepCommand,
1281 RepeatCommand,
1282 AutoReverseCommand,
1283 SaveCommand
1284 },
1285 SpeedCommands[]=
1286 {
1287 FasterCommand,
1288 SlowerCommand
1289 },
1290 DirectionCommands[]=
1291 {
1292 ForwardCommand,
1293 ReverseCommand
1294 },
1295 HelpCommands[]=
1296 {
1297 HelpCommand,
1298 BrowseDocumentationCommand,
1299 VersionCommand
1300 };
1301
1302 static const CommandType
1303 *Commands[MagickMenus]=
1304 {
1305 CommandTypes,
1306 SpeedCommands,
1307 DirectionCommands,
1308 HelpCommands
1309 };
1310
1311 char
1312 command[MaxTextExtent],
cristy00976d82011-02-20 20:31:28 +00001313 *directory,
cristy3ed852e2009-09-05 21:47:34 +00001314 geometry[MaxTextExtent],
1315 resource_name[MaxTextExtent];
1316
1317 CommandType
1318 command_type;
1319
1320 Image
1321 *coalesce_image,
1322 *display_image,
1323 *image,
1324 **image_list,
1325 *nexus;
1326
1327 int
1328 status;
1329
cristy3ed852e2009-09-05 21:47:34 +00001330 KeySym
1331 key_symbol;
1332
1333 MagickStatusType
1334 context_mask,
1335 state;
1336
1337 RectangleInfo
1338 geometry_info;
1339
1340 register char
1341 *p;
1342
cristybb503372010-05-27 20:51:26 +00001343 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001344 i;
1345
cristy9d314ff2011-03-09 01:30:28 +00001346 ssize_t
1347 first_scene,
1348 iterations,
1349 scene;
1350
cristy3ed852e2009-09-05 21:47:34 +00001351 static char
1352 working_directory[MaxTextExtent];
1353
cristybb503372010-05-27 20:51:26 +00001354 static size_t
cristy3ed852e2009-09-05 21:47:34 +00001355 number_windows;
1356
1357 static XWindowInfo
1358 *magick_windows[MaXWindows];
1359
1360 time_t
1361 timestamp;
1362
cristybb503372010-05-27 20:51:26 +00001363 size_t
cristy3ed852e2009-09-05 21:47:34 +00001364 delay,
1365 number_scenes;
1366
1367 WarningHandler
1368 warning_handler;
1369
1370 Window
1371 root_window;
1372
1373 XClassHint
1374 *class_hints;
1375
1376 XEvent
1377 event;
1378
1379 XFontStruct
1380 *font_info;
1381
1382 XGCValues
1383 context_values;
1384
1385 XPixelInfo
1386 *icon_pixel,
1387 *pixel;
1388
1389 XResourceInfo
1390 *icon_resources;
1391
1392 XStandardColormap
1393 *icon_map,
1394 *map_info;
1395
1396 XTextProperty
1397 window_name;
1398
1399 XVisualInfo
1400 *icon_visual,
1401 *visual_info;
1402
1403 XWindowChanges
1404 window_changes;
1405
1406 XWindows
1407 *windows;
1408
1409 XWMHints
1410 *manager_hints;
1411
1412 assert(images != (Image *) NULL);
1413 assert(images->signature == MagickSignature);
1414 if (images->debug != MagickFalse)
1415 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
1416 warning_handler=(WarningHandler) NULL;
1417 windows=XSetWindows((XWindows *) ~0);
1418 if (windows != (XWindows *) NULL)
1419 {
1420 int
1421 status;
1422
1423 status=chdir(working_directory);
1424 if (status == -1)
cristy051718b2011-08-28 22:49:25 +00001425 (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
1426 "UnableToOpenFile","%s",working_directory);
cristy3ed852e2009-09-05 21:47:34 +00001427 warning_handler=resource_info->display_warnings ?
1428 SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
1429 warning_handler=resource_info->display_warnings ?
1430 SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
1431 }
1432 else
1433 {
1434 register Image
1435 *p;
1436
1437 /*
1438 Initialize window structure.
1439 */
1440 for (p=images; p != (Image *) NULL; p=GetNextImageInList(p))
1441 {
1442 if (p->storage_class == DirectClass)
1443 {
1444 resource_info->colors=0;
1445 break;
1446 }
1447 if (p->colors > resource_info->colors)
1448 resource_info->colors=p->colors;
1449 }
1450 windows=XSetWindows(XInitializeWindows(display,resource_info));
1451 if (windows == (XWindows *) NULL)
1452 ThrowXWindowFatalException(XServerFatalError,"MemoryAllocationFailed",
1453 images->filename);
1454 /*
1455 Initialize window id's.
1456 */
1457 number_windows=0;
1458 magick_windows[number_windows++]=(&windows->icon);
1459 magick_windows[number_windows++]=(&windows->backdrop);
1460 magick_windows[number_windows++]=(&windows->image);
1461 magick_windows[number_windows++]=(&windows->info);
1462 magick_windows[number_windows++]=(&windows->command);
1463 magick_windows[number_windows++]=(&windows->widget);
1464 magick_windows[number_windows++]=(&windows->popup);
cristybb503372010-05-27 20:51:26 +00001465 for (i=0; i < (ssize_t) number_windows; i++)
cristy3ed852e2009-09-05 21:47:34 +00001466 magick_windows[i]->id=(Window) NULL;
1467 }
1468 /*
1469 Initialize font info.
1470 */
1471 if (windows->font_info != (XFontStruct *) NULL)
1472 (void) XFreeFont(display,windows->font_info);
1473 windows->font_info=XBestFont(display,resource_info,MagickFalse);
1474 if (windows->font_info == (XFontStruct *) NULL)
1475 ThrowXWindowFatalException(XServerFatalError,"UnableToLoadFont",
1476 resource_info->font);
1477 /*
1478 Initialize Standard Colormap.
1479 */
1480 map_info=windows->map_info;
1481 icon_map=windows->icon_map;
1482 visual_info=windows->visual_info;
1483 icon_visual=windows->icon_visual;
1484 pixel=windows->pixel_info;
1485 icon_pixel=windows->icon_pixel;
1486 font_info=windows->font_info;
1487 icon_resources=windows->icon_resources;
1488 class_hints=windows->class_hints;
1489 manager_hints=windows->manager_hints;
1490 root_window=XRootWindow(display,visual_info->screen);
cristy051718b2011-08-28 22:49:25 +00001491 coalesce_image=CoalesceImages(images,exception);
cristy3ed852e2009-09-05 21:47:34 +00001492 if (coalesce_image == (Image *) NULL)
1493 ThrowXWindowFatalException(XServerFatalError,"MemoryAllocationFailed",
1494 images->filename);
1495 images=coalesce_image;
1496 if (resource_info->map_type == (char *) NULL)
1497 if ((visual_info->klass != TrueColor) &&
1498 (visual_info->klass != DirectColor))
1499 {
1500 Image
1501 *next;
1502
1503 /*
1504 Determine if the sequence of images has the identical colormap.
1505 */
1506 for (next=images; next != (Image *) NULL; )
1507 {
cristy8a46d822012-08-28 23:32:39 +00001508 next->alpha_trait=UndefinedPixelTrait;
cristy3ed852e2009-09-05 21:47:34 +00001509 if ((next->storage_class == DirectClass) ||
1510 (next->colors != images->colors) ||
cristybb503372010-05-27 20:51:26 +00001511 (next->colors > (size_t) visual_info->colormap_size))
cristy3ed852e2009-09-05 21:47:34 +00001512 break;
cristybb503372010-05-27 20:51:26 +00001513 for (i=0; i < (ssize_t) images->colors; i++)
cristy101ab702011-10-13 13:06:32 +00001514 if (IsPixelInfoEquivalent(next->colormap+i,images->colormap+i) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001515 break;
cristybb503372010-05-27 20:51:26 +00001516 if (i < (ssize_t) images->colors)
cristy3ed852e2009-09-05 21:47:34 +00001517 break;
1518 next=GetNextImageInList(next);
1519 }
1520 if (next != (Image *) NULL)
1521 (void) RemapImages(resource_info->quantize_info,images,
cristy018f07f2011-09-04 21:15:19 +00001522 (Image *) NULL,exception);
cristy3ed852e2009-09-05 21:47:34 +00001523 }
1524 /*
1525 Sort images by increasing scene number.
1526 */
1527 number_scenes=GetImageListLength(images);
cristy051718b2011-08-28 22:49:25 +00001528 image_list=ImageListToArray(images,exception);
cristy3ed852e2009-09-05 21:47:34 +00001529 if (image_list == (Image **) NULL)
1530 ThrowXWindowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed",
1531 images->filename);
cristybb503372010-05-27 20:51:26 +00001532 for (scene=0; scene < (ssize_t) number_scenes; scene++)
cristy3ed852e2009-09-05 21:47:34 +00001533 if (image_list[scene]->scene == 0)
1534 break;
cristybb503372010-05-27 20:51:26 +00001535 if (scene == (ssize_t) number_scenes)
cristy3ed852e2009-09-05 21:47:34 +00001536 qsort((void *) image_list,number_scenes,sizeof(Image *),SceneCompare);
1537 /*
1538 Initialize Standard Colormap.
1539 */
1540 nexus=NewImageList();
1541 display_image=image_list[0];
cristybb503372010-05-27 20:51:26 +00001542 for (scene=0; scene < (ssize_t) number_scenes; scene++)
cristy3ed852e2009-09-05 21:47:34 +00001543 {
1544 if ((resource_info->map_type != (char *) NULL) ||
1545 (visual_info->klass == TrueColor) ||
1546 (visual_info->klass == DirectColor))
cristy8a46d822012-08-28 23:32:39 +00001547 (void) SetImageType(image_list[scene],image_list[scene]->alpha_trait ==
1548 BlendPixelTrait ? TrueColorType : TrueColorMatteType,exception);
cristy3ed852e2009-09-05 21:47:34 +00001549 if ((display_image->columns < image_list[scene]->columns) &&
1550 (display_image->rows < image_list[scene]->rows))
1551 display_image=image_list[scene];
1552 }
1553 if (display_image->debug != MagickFalse)
1554 {
1555 (void) LogMagickEvent(X11Event,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00001556 "Image: %s[%.20g] %.20gx%.20g ",display_image->filename,(double)
1557 display_image->scene,(double) display_image->columns,(double)
1558 display_image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001559 if (display_image->colors != 0)
cristye8c25f92010-06-03 00:53:06 +00001560 (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
1561 display_image->colors);
cristy3ed852e2009-09-05 21:47:34 +00001562 (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",
1563 display_image->magick);
1564 }
1565 XMakeStandardColormap(display,visual_info,resource_info,display_image,
cristy6710d842011-10-20 23:23:00 +00001566 map_info,pixel,exception);
cristy3ed852e2009-09-05 21:47:34 +00001567 /*
1568 Initialize graphic context.
1569 */
1570 windows->context.id=(Window) NULL;
1571 XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
1572 resource_info,&windows->context);
1573 (void) CloneString(&class_hints->res_name,resource_info->client_name);
1574 (void) CloneString(&class_hints->res_class,resource_info->client_name);
1575 class_hints->res_class[0]=(char) toupper((int) class_hints->res_class[0]);
1576 manager_hints->flags=InputHint | StateHint;
1577 manager_hints->input=MagickFalse;
1578 manager_hints->initial_state=WithdrawnState;
1579 XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
1580 &windows->context);
1581 if (display_image->debug != MagickFalse)
1582 (void) LogMagickEvent(X11Event,GetMagickModule(),
1583 "Window id: 0x%lx (context)",windows->context.id);
1584 context_values.background=pixel->background_color.pixel;
1585 context_values.font=font_info->fid;
1586 context_values.foreground=pixel->foreground_color.pixel;
1587 context_values.graphics_exposures=MagickFalse;
1588 context_mask=(MagickStatusType)
1589 (GCBackground | GCFont | GCForeground | GCGraphicsExposures);
1590 if (pixel->annotate_context != (GC) NULL)
1591 (void) XFreeGC(display,pixel->annotate_context);
1592 pixel->annotate_context=
1593 XCreateGC(display,windows->context.id,context_mask,&context_values);
1594 if (pixel->annotate_context == (GC) NULL)
1595 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
1596 images->filename);
1597 context_values.background=pixel->depth_color.pixel;
1598 if (pixel->widget_context != (GC) NULL)
1599 (void) XFreeGC(display,pixel->widget_context);
1600 pixel->widget_context=
1601 XCreateGC(display,windows->context.id,context_mask,&context_values);
1602 if (pixel->widget_context == (GC) NULL)
1603 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
1604 images->filename);
1605 context_values.background=pixel->foreground_color.pixel;
1606 context_values.foreground=pixel->background_color.pixel;
1607 context_values.plane_mask=
1608 context_values.background ^ context_values.foreground;
1609 if (pixel->highlight_context != (GC) NULL)
1610 (void) XFreeGC(display,pixel->highlight_context);
1611 pixel->highlight_context=XCreateGC(display,windows->context.id,
cristybb503372010-05-27 20:51:26 +00001612 (size_t) (context_mask | GCPlaneMask),&context_values);
cristy3ed852e2009-09-05 21:47:34 +00001613 if (pixel->highlight_context == (GC) NULL)
1614 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
1615 images->filename);
1616 (void) XDestroyWindow(display,windows->context.id);
1617 /*
1618 Initialize icon window.
1619 */
1620 XGetWindowInfo(display,icon_visual,icon_map,icon_pixel,(XFontStruct *) NULL,
1621 icon_resources,&windows->icon);
1622 windows->icon.geometry=resource_info->icon_geometry;
1623 XBestIconSize(display,&windows->icon,display_image);
1624 windows->icon.attributes.colormap=
1625 XDefaultColormap(display,icon_visual->screen);
1626 windows->icon.attributes.event_mask=ExposureMask | StructureNotifyMask;
1627 manager_hints->flags=InputHint | StateHint;
1628 manager_hints->input=MagickFalse;
1629 manager_hints->initial_state=IconicState;
1630 XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
1631 &windows->icon);
1632 if (display_image->debug != MagickFalse)
1633 (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (icon)",
1634 windows->icon.id);
1635 /*
1636 Initialize graphic context for icon window.
1637 */
1638 if (icon_pixel->annotate_context != (GC) NULL)
1639 (void) XFreeGC(display,icon_pixel->annotate_context);
1640 context_values.background=icon_pixel->background_color.pixel;
1641 context_values.foreground=icon_pixel->foreground_color.pixel;
1642 icon_pixel->annotate_context=XCreateGC(display,windows->icon.id,
cristybb503372010-05-27 20:51:26 +00001643 (size_t) (GCBackground | GCForeground),&context_values);
cristy3ed852e2009-09-05 21:47:34 +00001644 if (icon_pixel->annotate_context == (GC) NULL)
1645 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
1646 images->filename);
1647 windows->icon.annotate_context=icon_pixel->annotate_context;
1648 /*
1649 Initialize Image window.
1650 */
1651 XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
1652 resource_info,&windows->image);
1653 windows->image.shape=MagickTrue; /* non-rectangular shape hint */
1654 if (resource_info->use_shared_memory == MagickFalse)
1655 windows->image.shared_memory=MagickFalse;
1656 if (resource_info->title != (char *) NULL)
1657 {
1658 char
1659 *title;
1660
1661 title=InterpretImageProperties(resource_info->image_info,display_image,
cristy018f07f2011-09-04 21:15:19 +00001662 resource_info->title,exception);
cristy3ed852e2009-09-05 21:47:34 +00001663 (void) CopyMagickString(windows->image.name,title,MaxTextExtent);
1664 (void) CopyMagickString(windows->image.icon_name,title,MaxTextExtent);
1665 title=DestroyString(title);
1666 }
1667 else
1668 {
1669 char
1670 filename[MaxTextExtent];
1671
1672 /*
1673 Window name is the base of the filename.
1674 */
1675 GetPathComponent(display_image->magick_filename,TailPath,filename);
cristyb51dff52011-05-19 16:55:47 +00001676 (void) FormatLocaleString(windows->image.name,MaxTextExtent,
cristy04d55062011-03-15 18:58:50 +00001677 "%s: %s[scene: %.20g frames: %.20g]",MagickPackageName,filename,(double)
cristye8c25f92010-06-03 00:53:06 +00001678 display_image->scene,(double) number_scenes);
cristy3ed852e2009-09-05 21:47:34 +00001679 (void) CopyMagickString(windows->image.icon_name,filename,MaxTextExtent);
1680 }
1681 if (resource_info->immutable != MagickFalse)
1682 windows->image.immutable=MagickTrue;
1683 windows->image.shape=MagickTrue;
1684 windows->image.geometry=resource_info->image_geometry;
cristyb51dff52011-05-19 16:55:47 +00001685 (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>!",
cristy3ed852e2009-09-05 21:47:34 +00001686 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");
cristybb503372010-05-27 20:51:26 +00001710 windows->backdrop.flags=(size_t) (USSize | USPosition);
cristy3ed852e2009-09-05 21:47:34 +00001711 windows->backdrop.width=(unsigned int)
1712 XDisplayWidth(display,visual_info->screen);
1713 windows->backdrop.height=(unsigned int)
1714 XDisplayHeight(display,visual_info->screen);
1715 windows->backdrop.border_width=0;
1716 windows->backdrop.immutable=MagickTrue;
1717 windows->backdrop.attributes.do_not_propagate_mask=ButtonPressMask |
1718 ButtonReleaseMask;
1719 windows->backdrop.attributes.event_mask=ButtonPressMask | KeyPressMask |
1720 StructureNotifyMask;
1721 manager_hints->flags=IconWindowHint | InputHint | StateHint;
1722 manager_hints->icon_window=windows->icon.id;
1723 manager_hints->input=MagickTrue;
1724 manager_hints->initial_state=
1725 resource_info->iconic ? IconicState : NormalState;
1726 XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
1727 &windows->backdrop);
1728 if (display_image->debug != MagickFalse)
1729 (void) LogMagickEvent(X11Event,GetMagickModule(),
1730 "Window id: 0x%lx (backdrop)",windows->backdrop.id);
1731 (void) XMapWindow(display,windows->backdrop.id);
1732 (void) XClearWindow(display,windows->backdrop.id);
1733 if (windows->image.id != (Window) NULL)
1734 {
1735 (void) XDestroyWindow(display,windows->image.id);
1736 windows->image.id=(Window) NULL;
1737 }
1738 /*
1739 Position image in the center the backdrop.
1740 */
1741 windows->image.flags|=USPosition;
1742 windows->image.x=(XDisplayWidth(display,visual_info->screen)/2)-
1743 (windows->image.width/2);
1744 windows->image.y=(XDisplayHeight(display,visual_info->screen)/2)-
1745 (windows->image.height/2);
1746 }
1747 manager_hints->flags=IconWindowHint | InputHint | StateHint;
1748 manager_hints->icon_window=windows->icon.id;
1749 manager_hints->input=MagickTrue;
1750 manager_hints->initial_state=
1751 resource_info->iconic ? IconicState : NormalState;
1752 if (windows->group_leader.id != (Window) NULL)
1753 {
1754 /*
1755 Follow the leader.
1756 */
1757 manager_hints->flags|=(MagickStatusType) WindowGroupHint;
1758 manager_hints->window_group=windows->group_leader.id;
1759 (void) XSelectInput(display,windows->group_leader.id,StructureNotifyMask);
1760 if (display_image->debug != MagickFalse)
1761 (void) LogMagickEvent(X11Event,GetMagickModule(),
1762 "Window id: 0x%lx (group leader)",windows->group_leader.id);
1763 }
1764 XMakeWindow(display,
1765 (Window) (resource_info->backdrop ? windows->backdrop.id : root_window),
1766 argv,argc,class_hints,manager_hints,&windows->image);
1767 (void) XChangeProperty(display,windows->image.id,windows->im_protocols,
1768 XA_STRING,8,PropModeReplace,(unsigned char *) NULL,0);
1769 if (windows->group_leader.id != (Window) NULL)
1770 (void) XSetTransientForHint(display,windows->image.id,
1771 windows->group_leader.id);
1772 if (display_image->debug != MagickFalse)
1773 (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (image)",
1774 windows->image.id);
1775 /*
1776 Initialize Info widget.
1777 */
1778 XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
1779 resource_info,&windows->info);
1780 (void) CloneString(&windows->info.name,"Info");
1781 (void) CloneString(&windows->info.icon_name,"Info");
1782 windows->info.border_width=1;
1783 windows->info.x=2;
1784 windows->info.y=2;
1785 windows->info.flags|=PPosition;
1786 windows->info.attributes.win_gravity=UnmapGravity;
1787 windows->info.attributes.event_mask=ButtonPressMask | ExposureMask |
1788 StructureNotifyMask;
1789 manager_hints->flags=InputHint | StateHint | WindowGroupHint;
1790 manager_hints->input=MagickFalse;
1791 manager_hints->initial_state=NormalState;
1792 manager_hints->window_group=windows->image.id;
1793 XMakeWindow(display,windows->image.id,argv,argc,class_hints,manager_hints,
1794 &windows->info);
1795 windows->info.highlight_stipple=XCreateBitmapFromData(display,
1796 windows->info.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
1797 windows->info.shadow_stipple=XCreateBitmapFromData(display,
1798 windows->info.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
1799 (void) XSetTransientForHint(display,windows->info.id,windows->image.id);
1800 if (windows->image.mapped)
1801 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
1802 if (display_image->debug != MagickFalse)
1803 (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (info)",
1804 windows->info.id);
1805 /*
1806 Initialize Command widget.
1807 */
1808 XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
1809 resource_info,&windows->command);
1810 windows->command.data=MagickMenus;
1811 (void) XCommandWidget(display,windows,CommandMenu,(XEvent *) NULL);
cristyb51dff52011-05-19 16:55:47 +00001812 (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.command",
cristy3ed852e2009-09-05 21:47:34 +00001813 resource_info->client_name);
1814 windows->command.geometry=XGetResourceClass(resource_info->resource_database,
1815 resource_name,"geometry",(char *) NULL);
1816 (void) CloneString(&windows->command.name,MagickTitle);
1817 windows->command.border_width=0;
1818 windows->command.flags|=PPosition;
1819 windows->command.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
1820 ButtonReleaseMask | EnterWindowMask | ExposureMask | LeaveWindowMask |
1821 OwnerGrabButtonMask | StructureNotifyMask;
1822 manager_hints->flags=InputHint | StateHint | WindowGroupHint;
1823 manager_hints->input=MagickTrue;
1824 manager_hints->initial_state=NormalState;
1825 manager_hints->window_group=windows->image.id;
1826 XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
1827 &windows->command);
1828 windows->command.highlight_stipple=XCreateBitmapFromData(display,
1829 windows->command.id,(char *) HighlightBitmap,HighlightWidth,
1830 HighlightHeight);
1831 windows->command.shadow_stipple=XCreateBitmapFromData(display,
1832 windows->command.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
1833 (void) XSetTransientForHint(display,windows->command.id,windows->image.id);
1834 if (display_image->debug != MagickFalse)
1835 (void) LogMagickEvent(X11Event,GetMagickModule(),
1836 "Window id: 0x%lx (command)",windows->command.id);
1837 /*
1838 Initialize Widget window.
1839 */
1840 XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
1841 resource_info,&windows->widget);
cristyb51dff52011-05-19 16:55:47 +00001842 (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.widget",
cristy3ed852e2009-09-05 21:47:34 +00001843 resource_info->client_name);
1844 windows->widget.geometry=XGetResourceClass(resource_info->resource_database,
1845 resource_name,"geometry",(char *) NULL);
1846 windows->widget.border_width=0;
1847 windows->widget.flags|=PPosition;
1848 windows->widget.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
1849 ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
1850 KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
1851 StructureNotifyMask;
1852 manager_hints->flags=InputHint | StateHint | WindowGroupHint;
1853 manager_hints->input=MagickTrue;
1854 manager_hints->initial_state=NormalState;
1855 manager_hints->window_group=windows->image.id;
1856 XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
1857 &windows->widget);
1858 windows->widget.highlight_stipple=XCreateBitmapFromData(display,
1859 windows->widget.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
1860 windows->widget.shadow_stipple=XCreateBitmapFromData(display,
1861 windows->widget.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
1862 (void) XSetTransientForHint(display,windows->widget.id,windows->image.id);
1863 if (display_image->debug != MagickFalse)
1864 (void) LogMagickEvent(X11Event,GetMagickModule(),
1865 "Window id: 0x%lx (widget)",windows->widget.id);
1866 /*
1867 Initialize popup window.
1868 */
1869 XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
1870 resource_info,&windows->popup);
1871 windows->popup.border_width=0;
1872 windows->popup.flags|=PPosition;
1873 windows->popup.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
1874 ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
1875 KeyReleaseMask | LeaveWindowMask | StructureNotifyMask;
1876 manager_hints->flags=InputHint | StateHint | WindowGroupHint;
1877 manager_hints->input=MagickTrue;
1878 manager_hints->initial_state=NormalState;
1879 manager_hints->window_group=windows->image.id;
1880 XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
1881 &windows->popup);
1882 windows->popup.highlight_stipple=XCreateBitmapFromData(display,
1883 windows->popup.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
1884 windows->popup.shadow_stipple=XCreateBitmapFromData(display,
1885 windows->popup.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
1886 (void) XSetTransientForHint(display,windows->popup.id,windows->image.id);
1887 if (display_image->debug != MagickFalse)
1888 (void) LogMagickEvent(X11Event,GetMagickModule(),
1889 "Window id: 0x%lx (pop up)",windows->popup.id);
cristy3ed852e2009-09-05 21:47:34 +00001890 /*
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;
cristy3ed852e2009-09-05 21:47:34 +00001905 /*
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);
cristy3ed852e2009-09-05 21:47:34 +00001912 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);
cristy04e11fd2010-03-19 16:01:44 +00001920 if ((windows->image.mapped == MagickFalse) ||
1921 (windows->backdrop.id != (Window) NULL))
1922 (void) XMapWindow(display,windows->image.id);
1923 XSetCursorState(display,windows,MagickTrue);
cristybb503372010-05-27 20:51:26 +00001924 for (scene=0; scene < (ssize_t) number_scenes; scene++)
cristy3ed852e2009-09-05 21:47:34 +00001925 {
1926 unsigned int
1927 columns,
1928 rows;
1929
1930 /*
1931 Create X image.
1932 */
cristy3ed852e2009-09-05 21:47:34 +00001933 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)
cristy4c08aed2011-07-01 19:47:50 +00001939 XGetPixelInfo(display,visual_info,map_info,resource_info,
cristy3ed852e2009-09-05 21:47:34 +00001940 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],
cristy051718b2011-08-28 22:49:25 +00001948 columns,rows,exception);
cristy3ed852e2009-09-05 21:47:34 +00001949 if (status == MagickFalse)
1950 ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
1951 images->filename);
1952 if (image_list[scene]->debug != MagickFalse)
1953 {
1954 (void) LogMagickEvent(X11Event,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00001955 "Image: [%.20g] %s %.20gx%.20g ",(double) image_list[scene]->scene,
1956 image_list[scene]->filename,(double) columns,(double) rows);
cristy3ed852e2009-09-05 21:47:34 +00001957 if (image_list[scene]->colors != 0)
cristye8c25f92010-06-03 00:53:06 +00001958 (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
1959 image_list[scene]->colors);
cristy3ed852e2009-09-05 21:47:34 +00001960 (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,
cristy018f07f2011-09-04 21:15:19 +00001972 image_list[scene],resource_info->title,exception);
cristy3ed852e2009-09-05 21:47:34 +00001973 (void) CopyMagickString(windows->image.name,title,MaxTextExtent);
1974 title=DestroyString(title);
1975 }
1976 else
1977 {
1978 p=image_list[scene]->magick_filename+
1979 strlen(image_list[scene]->magick_filename)-1;
1980 while ((p > image_list[scene]->magick_filename) && (*(p-1) != '/'))
1981 p--;
cristyb51dff52011-05-19 16:55:47 +00001982 (void) FormatLocaleString(windows->image.name,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00001983 "%s: %s[%.20g of %.20g]",MagickPackageName,p,(double) scene+1,
1984 (double) number_scenes);
cristy3ed852e2009-09-05 21:47:34 +00001985 }
1986 status=XStringListToTextProperty(&windows->image.name,1,&window_name);
1987 if (status != Success)
1988 {
1989 XSetWMName(display,windows->image.id,&window_name);
1990 (void) XFree((void *) window_name.value);
1991 }
1992 windows->image.pixmaps[scene]=windows->image.pixmap;
1993 windows->image.matte_pixmaps[scene]=windows->image.matte_pixmap;
cristy04e11fd2010-03-19 16:01:44 +00001994 if (scene == 0)
cristy3ed852e2009-09-05 21:47:34 +00001995 {
cristy04e11fd2010-03-19 16:01:44 +00001996 event.xexpose.x=0;
1997 event.xexpose.y=0;
1998 event.xexpose.width=(int) image_list[scene]->columns;
1999 event.xexpose.height=(int) image_list[scene]->rows;
2000 XRefreshWindow(display,&windows->image,&event);
2001 (void) XSync(display,MagickFalse);
2002 }
cristy3ed852e2009-09-05 21:47:34 +00002003 }
cristy04e11fd2010-03-19 16:01:44 +00002004 XSetCursorState(display,windows,MagickFalse);
cristy3ed852e2009-09-05 21:47:34 +00002005 if (windows->command.mapped)
2006 (void) XMapRaised(display,windows->command.id);
2007 /*
2008 Respond to events.
2009 */
2010 nexus=NewImageList();
2011 scene=0;
2012 first_scene=0;
2013 iterations=0;
2014 image=image_list[0];
2015 state=(MagickStatusType) (ForwardAnimationState | RepeatAnimationState);
2016 (void) XMagickCommand(display,resource_info,windows,PlayCommand,&images,
cristy051718b2011-08-28 22:49:25 +00002017 &state,exception);
cristy3ed852e2009-09-05 21:47:34 +00002018 do
2019 {
2020 if (XEventsQueued(display,QueuedAfterFlush) == 0)
2021 if ((state & PlayAnimationState) || (state & StepAnimationState))
2022 {
2023 MagickBooleanType
2024 pause;
2025
2026 pause=MagickFalse;
2027 delay=1000*image->delay/MagickMax(image->ticks_per_second,1L);
2028 XDelay(display,resource_info->delay*(delay == 0 ? 10 : delay));
2029 if (state & ForwardAnimationState)
2030 {
2031 /*
2032 Forward animation: increment scene number.
2033 */
cristybb503372010-05-27 20:51:26 +00002034 if (scene < ((ssize_t) number_scenes-1))
cristy3ed852e2009-09-05 21:47:34 +00002035 scene++;
2036 else
2037 {
2038 iterations++;
cristybb503372010-05-27 20:51:26 +00002039 if (iterations == (ssize_t) image_list[0]->iterations)
cristy3ed852e2009-09-05 21:47:34 +00002040 {
2041 iterations=0;
2042 state|=ExitState;
2043 }
cristy04e11fd2010-03-19 16:01:44 +00002044 if ((state & AutoReverseAnimationState) != 0)
cristy3ed852e2009-09-05 21:47:34 +00002045 {
2046 state&=(~ForwardAnimationState);
2047 scene--;
2048 }
2049 else
2050 {
cristy04e11fd2010-03-19 16:01:44 +00002051 if ((state & RepeatAnimationState) == 0)
cristy3ed852e2009-09-05 21:47:34 +00002052 state&=(~PlayAnimationState);
2053 scene=first_scene;
2054 pause=MagickTrue;
2055 }
2056 }
2057 }
2058 else
2059 {
2060 /*
2061 Reverse animation: decrement scene number.
2062 */
2063 if (scene > first_scene)
2064 scene--;
2065 else
2066 {
2067 iterations++;
cristybb503372010-05-27 20:51:26 +00002068 if (iterations == (ssize_t) image_list[0]->iterations)
cristy3ed852e2009-09-05 21:47:34 +00002069 {
2070 iterations=0;
2071 state&=(~RepeatAnimationState);
2072 }
2073 if (state & AutoReverseAnimationState)
2074 {
2075 state|=ForwardAnimationState;
2076 scene=first_scene;
2077 pause=MagickTrue;
2078 }
2079 else
2080 {
2081 if ((state & RepeatAnimationState) == MagickFalse)
2082 state&=(~PlayAnimationState);
cristybb503372010-05-27 20:51:26 +00002083 scene=(ssize_t) number_scenes-1;
cristy3ed852e2009-09-05 21:47:34 +00002084 }
2085 }
2086 }
2087 scene=MagickMax(scene,0);
2088 image=image_list[scene];
2089 if ((image != (Image *) NULL) && (image->start_loop != 0))
2090 first_scene=scene;
2091 if ((state & StepAnimationState) ||
2092 (resource_info->title != (char *) NULL))
2093 {
2094 /*
2095 Update window title.
2096 */
2097 p=image_list[scene]->filename+
2098 strlen(image_list[scene]->filename)-1;
2099 while ((p > image_list[scene]->filename) && (*(p-1) != '/'))
2100 p--;
cristyb51dff52011-05-19 16:55:47 +00002101 (void) FormatLocaleString(windows->image.name,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00002102 "%s: %s[%.20g of %.20g]",MagickPackageName,p,(double)
2103 scene+1,(double) number_scenes);
cristy3ed852e2009-09-05 21:47:34 +00002104 if (resource_info->title != (char *) NULL)
2105 {
2106 char
2107 *title;
2108
2109 title=InterpretImageProperties(resource_info->image_info,
cristy018f07f2011-09-04 21:15:19 +00002110 image,resource_info->title,exception);
cristy3ed852e2009-09-05 21:47:34 +00002111 (void) CopyMagickString(windows->image.name,title,
2112 MaxTextExtent);
2113 title=DestroyString(title);
2114 }
2115 status=XStringListToTextProperty(&windows->image.name,1,
2116 &window_name);
2117 if (status != Success)
2118 {
2119 XSetWMName(display,windows->image.id,&window_name);
2120 (void) XFree((void *) window_name.value);
2121 }
2122 }
2123 /*
2124 Copy X pixmap to Image window.
2125 */
cristy4c08aed2011-07-01 19:47:50 +00002126 XGetPixelInfo(display,visual_info,map_info,resource_info,
cristy3ed852e2009-09-05 21:47:34 +00002127 image_list[scene],windows->image.pixel_info);
2128 windows->image.ximage->width=(int) image->columns;
2129 windows->image.ximage->height=(int) image->rows;
2130 windows->image.pixmap=windows->image.pixmaps[scene];
2131 windows->image.matte_pixmap=windows->image.matte_pixmaps[scene];
2132 event.xexpose.x=0;
2133 event.xexpose.y=0;
2134 event.xexpose.width=(int) image->columns;
2135 event.xexpose.height=(int) image->rows;
cristy04e11fd2010-03-19 16:01:44 +00002136 if ((state & ExitState) == 0)
2137 {
2138 XRefreshWindow(display,&windows->image,&event);
2139 (void) XSync(display,MagickFalse);
2140 }
cristy3ed852e2009-09-05 21:47:34 +00002141 state&=(~StepAnimationState);
2142 if (pause != MagickFalse)
cristybb503372010-05-27 20:51:26 +00002143 for (i=0; i < (ssize_t) resource_info->pause; i++)
cristy3ed852e2009-09-05 21:47:34 +00002144 {
2145 int
2146 status;
2147
2148 status=XCheckTypedWindowEvent(display,windows->image.id,KeyPress,
2149 &event);
2150 if (status != 0)
2151 {
2152 int
2153 length;
2154
2155 length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
2156 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2157 *(command+length)='\0';
2158 if ((key_symbol == XK_q) || (key_symbol == XK_Escape))
2159 {
2160 XClientMessage(display,windows->image.id,
2161 windows->im_protocols,windows->im_exit,CurrentTime);
2162 break;
2163 }
2164 }
2165 (void) sleep(1);
2166 }
2167 continue;
2168 }
2169 /*
2170 Handle a window event.
2171 */
2172 timestamp=time((time_t *) NULL);
2173 (void) XNextEvent(display,&event);
2174 if (windows->image.stasis == MagickFalse)
2175 windows->image.stasis=(time((time_t *) NULL)-timestamp) > 0 ?
2176 MagickTrue : MagickFalse;
2177 if (event.xany.window == windows->command.id)
2178 {
2179 int
2180 id;
2181
2182 /*
2183 Select a command from the Command widget.
2184 */
2185 id=XCommandWidget(display,windows,CommandMenu,&event);
2186 if (id < 0)
2187 continue;
2188 (void) CopyMagickString(command,CommandMenu[id],MaxTextExtent);
2189 command_type=CommandMenus[id];
2190 if (id < MagickMenus)
2191 {
2192 int
2193 entry;
2194
2195 /*
2196 Select a command from a pop-up menu.
2197 */
2198 entry=XMenuWidget(display,windows,CommandMenu[id],Menus[id],
2199 command);
2200 if (entry < 0)
2201 continue;
2202 (void) CopyMagickString(command,Menus[id][entry],MaxTextExtent);
2203 command_type=Commands[id][entry];
2204 }
2205 if (command_type != NullCommand)
2206 nexus=XMagickCommand(display,resource_info,windows,
cristy051718b2011-08-28 22:49:25 +00002207 command_type,&image,&state,exception);
cristy3ed852e2009-09-05 21:47:34 +00002208 continue;
2209 }
2210 switch (event.type)
2211 {
2212 case ButtonPress:
2213 {
2214 if (display_image->debug != MagickFalse)
2215 (void) LogMagickEvent(X11Event,GetMagickModule(),
2216 "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
2217 event.xbutton.button,event.xbutton.x,event.xbutton.y);
2218 if ((event.xbutton.button == Button3) &&
2219 (event.xbutton.state & Mod1Mask))
2220 {
2221 /*
2222 Convert Alt-Button3 to Button2.
2223 */
2224 event.xbutton.button=Button2;
2225 event.xbutton.state&=(~Mod1Mask);
2226 }
2227 if (event.xbutton.window == windows->backdrop.id)
2228 {
2229 (void) XSetInputFocus(display,event.xbutton.window,RevertToParent,
2230 event.xbutton.time);
2231 break;
2232 }
2233 if (event.xbutton.window == windows->image.id)
2234 {
2235 if (resource_info->immutable != MagickFalse)
2236 {
2237 state|=ExitState;
2238 break;
2239 }
2240 /*
2241 Map/unmap Command widget.
2242 */
2243 if (windows->command.mapped)
2244 (void) XWithdrawWindow(display,windows->command.id,
2245 windows->command.screen);
2246 else
2247 {
2248 (void) XCommandWidget(display,windows,CommandMenu,
2249 (XEvent *) NULL);
2250 (void) XMapRaised(display,windows->command.id);
2251 }
2252 }
2253 break;
2254 }
2255 case ButtonRelease:
2256 {
2257 if (display_image->debug != MagickFalse)
2258 (void) LogMagickEvent(X11Event,GetMagickModule(),
2259 "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
2260 event.xbutton.button,event.xbutton.x,event.xbutton.y);
2261 break;
2262 }
2263 case ClientMessage:
2264 {
2265 if (display_image->debug != MagickFalse)
2266 (void) LogMagickEvent(X11Event,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00002267 "Client Message: 0x%lx 0x%lx %d 0x%lx",(unsigned long)
2268 event.xclient.window,(unsigned long) event.xclient.message_type,
2269 event.xclient.format,(unsigned long) event.xclient.data.l[0]);
cristy3ed852e2009-09-05 21:47:34 +00002270 if (event.xclient.message_type == windows->im_protocols)
2271 {
cristyecd0ab52010-05-30 14:59:20 +00002272 if (*event.xclient.data.l == (long) windows->im_update_colormap)
cristy3ed852e2009-09-05 21:47:34 +00002273 {
2274 /*
2275 Update graphic context and window colormap.
2276 */
cristybb503372010-05-27 20:51:26 +00002277 for (i=0; i < (ssize_t) number_windows; i++)
cristy3ed852e2009-09-05 21:47:34 +00002278 {
2279 if (magick_windows[i]->id == windows->icon.id)
2280 continue;
2281 context_values.background=pixel->background_color.pixel;
2282 context_values.foreground=pixel->foreground_color.pixel;
2283 (void) XChangeGC(display,magick_windows[i]->annotate_context,
2284 context_mask,&context_values);
2285 (void) XChangeGC(display,magick_windows[i]->widget_context,
2286 context_mask,&context_values);
2287 context_values.background=pixel->foreground_color.pixel;
2288 context_values.foreground=pixel->background_color.pixel;
2289 context_values.plane_mask=
2290 context_values.background ^ context_values.foreground;
2291 (void) XChangeGC(display,magick_windows[i]->highlight_context,
cristybb503372010-05-27 20:51:26 +00002292 (size_t) (context_mask | GCPlaneMask),
cristy3ed852e2009-09-05 21:47:34 +00002293 &context_values);
2294 magick_windows[i]->attributes.background_pixel=
2295 pixel->background_color.pixel;
2296 magick_windows[i]->attributes.border_pixel=
2297 pixel->border_color.pixel;
2298 magick_windows[i]->attributes.colormap=map_info->colormap;
2299 (void) XChangeWindowAttributes(display,magick_windows[i]->id,
cristy49e2d862010-11-12 02:50:30 +00002300 (unsigned long) magick_windows[i]->mask,
2301 &magick_windows[i]->attributes);
cristy3ed852e2009-09-05 21:47:34 +00002302 }
2303 if (windows->backdrop.id != (Window) NULL)
2304 (void) XInstallColormap(display,map_info->colormap);
2305 break;
2306 }
cristyecd0ab52010-05-30 14:59:20 +00002307 if (*event.xclient.data.l == (long) windows->im_exit)
cristy3ed852e2009-09-05 21:47:34 +00002308 {
2309 state|=ExitState;
2310 break;
2311 }
2312 break;
2313 }
2314 if (event.xclient.message_type == windows->dnd_protocols)
2315 {
2316 Atom
2317 selection,
2318 type;
2319
2320 int
2321 format,
2322 status;
2323
2324 unsigned char
2325 *data;
2326
cristyf2faecf2010-05-28 19:19:36 +00002327 unsigned long
cristy3ed852e2009-09-05 21:47:34 +00002328 after,
2329 length;
2330
2331 /*
2332 Display image named by the Drag-and-Drop selection.
2333 */
2334 if ((*event.xclient.data.l != 2) && (*event.xclient.data.l != 128))
2335 break;
2336 selection=XInternAtom(display,"DndSelection",MagickFalse);
2337 status=XGetWindowProperty(display,root_window,selection,0L,2047L,
2338 MagickFalse,(Atom) AnyPropertyType,&type,&format,&length,&after,
2339 &data);
2340 if ((status != Success) || (length == 0))
2341 break;
2342 if (*event.xclient.data.l == 2)
2343 {
2344 /*
2345 Offix DND.
2346 */
2347 (void) CopyMagickString(resource_info->image_info->filename,
2348 (char *) data,MaxTextExtent);
2349 }
2350 else
2351 {
2352 /*
2353 XDND.
2354 */
2355 if (LocaleNCompare((char *) data,"file:",5) != 0)
2356 {
2357 (void) XFree((void *) data);
2358 break;
2359 }
2360 (void) CopyMagickString(resource_info->image_info->filename,
2361 ((char *) data)+5,MaxTextExtent);
2362 }
cristy051718b2011-08-28 22:49:25 +00002363 nexus=ReadImage(resource_info->image_info,exception);
2364 CatchException(exception);
cristy3ed852e2009-09-05 21:47:34 +00002365 if (nexus != (Image *) NULL)
2366 state|=ExitState;
2367 (void) XFree((void *) data);
2368 break;
2369 }
2370 /*
2371 If client window delete message, exit.
2372 */
2373 if (event.xclient.message_type != windows->wm_protocols)
2374 break;
cristycee97112010-05-28 00:44:52 +00002375 if (*event.xclient.data.l == (long) windows->wm_take_focus)
cristy3ed852e2009-09-05 21:47:34 +00002376 {
2377 (void) XSetInputFocus(display,event.xclient.window,RevertToParent,
2378 (Time) event.xclient.data.l[1]);
2379 break;
2380 }
cristycee97112010-05-28 00:44:52 +00002381 if (*event.xclient.data.l != (long) windows->wm_delete_window)
cristy3ed852e2009-09-05 21:47:34 +00002382 break;
2383 (void) XWithdrawWindow(display,event.xclient.window,
2384 visual_info->screen);
2385 if (event.xclient.window == windows->image.id)
2386 {
2387 state|=ExitState;
2388 break;
2389 }
2390 break;
2391 }
2392 case ConfigureNotify:
2393 {
2394 if (display_image->debug != MagickFalse)
2395 (void) LogMagickEvent(X11Event,GetMagickModule(),
2396 "Configure Notify: 0x%lx %dx%d+%d+%d %d",event.xconfigure.window,
2397 event.xconfigure.width,event.xconfigure.height,event.xconfigure.x,
2398 event.xconfigure.y,event.xconfigure.send_event);
2399 if (event.xconfigure.window == windows->image.id)
2400 {
2401 if (event.xconfigure.send_event != 0)
2402 {
2403 XWindowChanges
2404 window_changes;
2405
2406 /*
2407 Position the transient windows relative of the Image window.
2408 */
2409 if (windows->command.geometry == (char *) NULL)
2410 if (windows->command.mapped == MagickFalse)
2411 {
2412 windows->command.x=
2413 event.xconfigure.x-windows->command.width-25;
2414 windows->command.y=event.xconfigure.y;
2415 XConstrainWindowPosition(display,&windows->command);
2416 window_changes.x=windows->command.x;
2417 window_changes.y=windows->command.y;
2418 (void) XReconfigureWMWindow(display,windows->command.id,
2419 windows->command.screen,(unsigned int) (CWX | CWY),
2420 &window_changes);
2421 }
2422 if (windows->widget.geometry == (char *) NULL)
2423 if (windows->widget.mapped == MagickFalse)
2424 {
2425 windows->widget.x=
2426 event.xconfigure.x+event.xconfigure.width/10;
2427 windows->widget.y=
2428 event.xconfigure.y+event.xconfigure.height/10;
2429 XConstrainWindowPosition(display,&windows->widget);
2430 window_changes.x=windows->widget.x;
2431 window_changes.y=windows->widget.y;
2432 (void) XReconfigureWMWindow(display,windows->widget.id,
2433 windows->widget.screen,(unsigned int) (CWX | CWY),
2434 &window_changes);
2435 }
2436 }
2437 /*
2438 Image window has a new configuration.
2439 */
2440 windows->image.width=(unsigned int) event.xconfigure.width;
2441 windows->image.height=(unsigned int) event.xconfigure.height;
2442 break;
2443 }
2444 if (event.xconfigure.window == windows->icon.id)
2445 {
2446 /*
2447 Icon window has a new configuration.
2448 */
2449 windows->icon.width=(unsigned int) event.xconfigure.width;
2450 windows->icon.height=(unsigned int) event.xconfigure.height;
2451 break;
2452 }
2453 break;
2454 }
2455 case DestroyNotify:
2456 {
2457 /*
2458 Group leader has exited.
2459 */
2460 if (display_image->debug != MagickFalse)
2461 (void) LogMagickEvent(X11Event,GetMagickModule(),
2462 "Destroy Notify: 0x%lx",event.xdestroywindow.window);
2463 if (event.xdestroywindow.window == windows->group_leader.id)
2464 {
2465 state|=ExitState;
2466 break;
2467 }
2468 break;
2469 }
2470 case EnterNotify:
2471 {
2472 /*
2473 Selectively install colormap.
2474 */
2475 if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
2476 if (event.xcrossing.mode != NotifyUngrab)
2477 XInstallColormap(display,map_info->colormap);
2478 break;
2479 }
2480 case Expose:
2481 {
2482 if (display_image->debug != MagickFalse)
2483 (void) LogMagickEvent(X11Event,GetMagickModule(),
2484 "Expose: 0x%lx %dx%d+%d+%d",event.xexpose.window,
2485 event.xexpose.width,event.xexpose.height,event.xexpose.x,
2486 event.xexpose.y);
2487 /*
2488 Repaint windows that are now exposed.
2489 */
2490 if (event.xexpose.window == windows->image.id)
2491 {
2492 windows->image.pixmap=windows->image.pixmaps[scene];
2493 windows->image.matte_pixmap=windows->image.matte_pixmaps[scene];
2494 XRefreshWindow(display,&windows->image,&event);
2495 break;
2496 }
2497 if (event.xexpose.window == windows->icon.id)
2498 if (event.xexpose.count == 0)
2499 {
2500 XRefreshWindow(display,&windows->icon,&event);
2501 break;
2502 }
2503 break;
2504 }
2505 case KeyPress:
2506 {
2507 static int
2508 length;
2509
2510 /*
2511 Respond to a user key press.
2512 */
2513 length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
2514 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2515 *(command+length)='\0';
2516 if (display_image->debug != MagickFalse)
2517 (void) LogMagickEvent(X11Event,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00002518 "Key press: 0x%lx (%c)",(unsigned long) key_symbol,*command);
cristy3ed852e2009-09-05 21:47:34 +00002519 command_type=NullCommand;
2520 switch (key_symbol)
2521 {
2522 case XK_o:
2523 {
2524 if ((event.xkey.state & ControlMask) == MagickFalse)
2525 break;
2526 command_type=OpenCommand;
2527 break;
2528 }
2529 case XK_BackSpace:
2530 {
2531 command_type=StepBackwardCommand;
2532 break;
2533 }
2534 case XK_space:
2535 {
2536 command_type=StepForwardCommand;
2537 break;
2538 }
2539 case XK_less:
2540 {
2541 command_type=FasterCommand;
2542 break;
2543 }
2544 case XK_greater:
2545 {
2546 command_type=SlowerCommand;
2547 break;
2548 }
2549 case XK_F1:
2550 {
2551 command_type=HelpCommand;
2552 break;
2553 }
2554 case XK_Find:
2555 {
2556 command_type=BrowseDocumentationCommand;
2557 break;
2558 }
2559 case XK_question:
2560 {
2561 command_type=InfoCommand;
2562 break;
2563 }
2564 case XK_q:
2565 case XK_Escape:
2566 {
2567 command_type=QuitCommand;
2568 break;
2569 }
2570 default:
2571 break;
2572 }
2573 if (command_type != NullCommand)
2574 nexus=XMagickCommand(display,resource_info,windows,
cristy051718b2011-08-28 22:49:25 +00002575 command_type,&image,&state,exception);
cristy3ed852e2009-09-05 21:47:34 +00002576 break;
2577 }
2578 case KeyRelease:
2579 {
2580 /*
2581 Respond to a user key release.
2582 */
2583 (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2584 sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2585 if (display_image->debug != MagickFalse)
2586 (void) LogMagickEvent(X11Event,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00002587 "Key release: 0x%lx (%c)",(unsigned long) key_symbol,*command);
cristy3ed852e2009-09-05 21:47:34 +00002588 break;
2589 }
2590 case LeaveNotify:
2591 {
2592 /*
2593 Selectively uninstall colormap.
2594 */
2595 if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
2596 if (event.xcrossing.mode != NotifyUngrab)
2597 XUninstallColormap(display,map_info->colormap);
2598 break;
2599 }
2600 case MapNotify:
2601 {
2602 if (display_image->debug != MagickFalse)
2603 (void) LogMagickEvent(X11Event,GetMagickModule(),"Map Notify: 0x%lx",
2604 event.xmap.window);
2605 if (event.xmap.window == windows->backdrop.id)
2606 {
2607 (void) XSetInputFocus(display,event.xmap.window,RevertToParent,
2608 CurrentTime);
2609 windows->backdrop.mapped=MagickTrue;
2610 break;
2611 }
2612 if (event.xmap.window == windows->image.id)
2613 {
2614 if (windows->backdrop.id != (Window) NULL)
2615 (void) XInstallColormap(display,map_info->colormap);
2616 if (LocaleCompare(image_list[0]->magick,"LOGO") == 0)
2617 {
2618 if (LocaleCompare(display_image->filename,"LOGO") == 0)
2619 nexus=XMagickCommand(display,resource_info,windows,
cristy051718b2011-08-28 22:49:25 +00002620 OpenCommand,&image,&state,exception);
cristy3ed852e2009-09-05 21:47:34 +00002621 else
2622 state|=ExitState;
2623 }
2624 windows->image.mapped=MagickTrue;
2625 break;
2626 }
2627 if (event.xmap.window == windows->info.id)
2628 {
2629 windows->info.mapped=MagickTrue;
2630 break;
2631 }
2632 if (event.xmap.window == windows->icon.id)
2633 {
2634 /*
2635 Create an icon image.
2636 */
2637 XMakeStandardColormap(display,icon_visual,icon_resources,
cristy6710d842011-10-20 23:23:00 +00002638 display_image,icon_map,icon_pixel,exception);
cristy3ed852e2009-09-05 21:47:34 +00002639 (void) XMakeImage(display,icon_resources,&windows->icon,
cristy051718b2011-08-28 22:49:25 +00002640 display_image,windows->icon.width,windows->icon.height,
2641 exception);
cristy3ed852e2009-09-05 21:47:34 +00002642 (void) XSetWindowBackgroundPixmap(display,windows->icon.id,
2643 windows->icon.pixmap);
2644 (void) XClearWindow(display,windows->icon.id);
2645 (void) XWithdrawWindow(display,windows->info.id,
2646 windows->info.screen);
2647 windows->icon.mapped=MagickTrue;
2648 break;
2649 }
2650 if (event.xmap.window == windows->command.id)
2651 {
2652 windows->command.mapped=MagickTrue;
2653 break;
2654 }
2655 if (event.xmap.window == windows->popup.id)
2656 {
2657 windows->popup.mapped=MagickTrue;
2658 break;
2659 }
2660 if (event.xmap.window == windows->widget.id)
2661 {
2662 windows->widget.mapped=MagickTrue;
2663 break;
2664 }
2665 break;
2666 }
2667 case MappingNotify:
2668 {
2669 (void) XRefreshKeyboardMapping(&event.xmapping);
2670 break;
2671 }
2672 case NoExpose:
2673 break;
2674 case PropertyNotify:
2675 {
2676 Atom
2677 type;
2678
2679 int
2680 format,
2681 status;
2682
2683 unsigned char
2684 *data;
2685
cristyf2faecf2010-05-28 19:19:36 +00002686 unsigned long
cristy3ed852e2009-09-05 21:47:34 +00002687 after,
2688 length;
2689
2690 if (display_image->debug != MagickFalse)
2691 (void) LogMagickEvent(X11Event,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00002692 "Property Notify: 0x%lx 0x%lx %d",(unsigned long)
2693 event.xproperty.window,(unsigned long) event.xproperty.atom,
2694 event.xproperty.state);
cristy3ed852e2009-09-05 21:47:34 +00002695 if (event.xproperty.atom != windows->im_remote_command)
2696 break;
2697 /*
2698 Display image named by the remote command protocol.
2699 */
2700 status=XGetWindowProperty(display,event.xproperty.window,
cristycee97112010-05-28 00:44:52 +00002701 event.xproperty.atom,0L,(long) MaxTextExtent,MagickFalse,(Atom)
cristy3ed852e2009-09-05 21:47:34 +00002702 AnyPropertyType,&type,&format,&length,&after,&data);
2703 if ((status != Success) || (length == 0))
2704 break;
2705 (void) CopyMagickString(resource_info->image_info->filename,
2706 (char *) data,MaxTextExtent);
cristy051718b2011-08-28 22:49:25 +00002707 nexus=ReadImage(resource_info->image_info,exception);
2708 CatchException(exception);
cristy3ed852e2009-09-05 21:47:34 +00002709 if (nexus != (Image *) NULL)
2710 state|=ExitState;
2711 (void) XFree((void *) data);
2712 break;
2713 }
2714 case ReparentNotify:
2715 {
2716 if (display_image->debug != MagickFalse)
2717 (void) LogMagickEvent(X11Event,GetMagickModule(),
2718 "Reparent Notify: 0x%lx=>0x%lx",event.xreparent.parent,
2719 event.xreparent.window);
2720 break;
2721 }
2722 case UnmapNotify:
2723 {
2724 if (display_image->debug != MagickFalse)
2725 (void) LogMagickEvent(X11Event,GetMagickModule(),
2726 "Unmap Notify: 0x%lx",event.xunmap.window);
2727 if (event.xunmap.window == windows->backdrop.id)
2728 {
2729 windows->backdrop.mapped=MagickFalse;
2730 break;
2731 }
2732 if (event.xunmap.window == windows->image.id)
2733 {
2734 windows->image.mapped=MagickFalse;
2735 break;
2736 }
2737 if (event.xunmap.window == windows->info.id)
2738 {
2739 windows->info.mapped=MagickFalse;
2740 break;
2741 }
2742 if (event.xunmap.window == windows->icon.id)
2743 {
2744 if (map_info->colormap == icon_map->colormap)
2745 XConfigureImageColormap(display,resource_info,windows,
cristy6710d842011-10-20 23:23:00 +00002746 display_image,exception);
cristy3ed852e2009-09-05 21:47:34 +00002747 (void) XFreeStandardColormap(display,icon_visual,icon_map,
2748 icon_pixel);
2749 windows->icon.mapped=MagickFalse;
2750 break;
2751 }
2752 if (event.xunmap.window == windows->command.id)
2753 {
2754 windows->command.mapped=MagickFalse;
2755 break;
2756 }
2757 if (event.xunmap.window == windows->popup.id)
2758 {
2759 if (windows->backdrop.id != (Window) NULL)
2760 (void) XSetInputFocus(display,windows->image.id,RevertToParent,
2761 CurrentTime);
2762 windows->popup.mapped=MagickFalse;
2763 break;
2764 }
2765 if (event.xunmap.window == windows->widget.id)
2766 {
2767 if (windows->backdrop.id != (Window) NULL)
2768 (void) XSetInputFocus(display,windows->image.id,RevertToParent,
2769 CurrentTime);
2770 windows->widget.mapped=MagickFalse;
2771 break;
2772 }
2773 break;
2774 }
2775 default:
2776 {
2777 if (display_image->debug != MagickFalse)
2778 (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
2779 event.type);
2780 break;
2781 }
2782 }
2783 }
2784 while (!(state & ExitState));
2785 image_list=(Image **) RelinquishMagickMemory(image_list);
2786 images=DestroyImageList(images);
2787 if ((windows->visual_info->klass == GrayScale) ||
2788 (windows->visual_info->klass == PseudoColor) ||
2789 (windows->visual_info->klass == DirectColor))
2790 {
2791 /*
2792 Withdraw windows.
2793 */
2794 if (windows->info.mapped)
2795 (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
2796 if (windows->command.mapped)
2797 (void) XWithdrawWindow(display,windows->command.id,
2798 windows->command.screen);
2799 }
2800 if (resource_info->backdrop == MagickFalse)
2801 if (windows->backdrop.mapped)
2802 {
2803 (void) XWithdrawWindow(display,windows->backdrop.id,\
2804 windows->backdrop.screen);
2805 (void) XDestroyWindow(display,windows->backdrop.id);
2806 windows->backdrop.id=(Window) NULL;
2807 (void) XWithdrawWindow(display,windows->image.id,windows->image.screen);
2808 (void) XDestroyWindow(display,windows->image.id);
2809 windows->image.id=(Window) NULL;
2810 }
2811 XSetCursorState(display,windows,MagickTrue);
2812 XCheckRefreshWindows(display,windows);
cristybb503372010-05-27 20:51:26 +00002813 for (scene=1; scene < (ssize_t) number_scenes; scene++)
cristy3ed852e2009-09-05 21:47:34 +00002814 {
2815 if (windows->image.pixmaps[scene] != (Pixmap) NULL)
2816 (void) XFreePixmap(display,windows->image.pixmaps[scene]);
2817 windows->image.pixmaps[scene]=(Pixmap) NULL;
2818 if (windows->image.matte_pixmaps[scene] != (Pixmap) NULL)
2819 (void) XFreePixmap(display,windows->image.matte_pixmaps[scene]);
2820 windows->image.matte_pixmaps[scene]=(Pixmap) NULL;
2821 }
cristy04e11fd2010-03-19 16:01:44 +00002822 XSetCursorState(display,windows,MagickFalse);
cristy3ed852e2009-09-05 21:47:34 +00002823 windows->image.pixmaps=(Pixmap *)
2824 RelinquishMagickMemory(windows->image.pixmaps);
2825 windows->image.matte_pixmaps=(Pixmap *)
2826 RelinquishMagickMemory(windows->image.matte_pixmaps);
2827 if (nexus == (Image *) NULL)
2828 {
2829 /*
2830 Free X resources.
2831 */
2832 if (windows->image.mapped != MagickFalse)
2833 (void) XWithdrawWindow(display,windows->image.id,windows->image.screen); XDelay(display,SuspendTime);
2834 (void) XFreeStandardColormap(display,icon_visual,icon_map,icon_pixel);
2835 if (resource_info->map_type == (char *) NULL)
2836 (void) XFreeStandardColormap(display,visual_info,map_info,pixel);
2837 DestroyXResources();
2838 }
2839 (void) XSync(display,MagickFalse);
2840 /*
2841 Restore our progress monitor and warning handlers.
2842 */
2843 (void) SetErrorHandler(warning_handler);
2844 (void) SetWarningHandler(warning_handler);
2845 /*
2846 Change to home directory.
2847 */
cristy00976d82011-02-20 20:31:28 +00002848 directory=getcwd(working_directory,MaxTextExtent);
2849 (void) directory;
cristy3ed852e2009-09-05 21:47:34 +00002850 status=chdir(resource_info->home_directory);
2851 if (status == -1)
cristy051718b2011-08-28 22:49:25 +00002852 (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
2853 "UnableToOpenFile","%s",resource_info->home_directory);
cristy3ed852e2009-09-05 21:47:34 +00002854 return(nexus);
2855}
2856
2857/*
2858%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2859% %
2860% %
2861% %
2862+ X S a v e I m a g e %
2863% %
2864% %
2865% %
2866%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2867%
2868% XSaveImage() saves an image to a file.
2869%
2870% The format of the XSaveImage method is:
2871%
2872% MagickBooleanType XSaveImage(Display *display,
cristy051718b2011-08-28 22:49:25 +00002873% XResourceInfo *resource_info,XWindows *windows,Image *image,
2874% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002875%
2876% A description of each parameter follows:
2877%
2878% o status: Method XSaveImage return True if the image is
2879% written. False is returned is there is a memory shortage or if the
2880% image fails to write.
2881%
2882% o display: Specifies a connection to an X server; returned from
2883% XOpenDisplay.
2884%
2885% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2886%
2887% o windows: Specifies a pointer to a XWindows structure.
2888%
2889% o image: the image.
2890%
2891*/
2892static MagickBooleanType XSaveImage(Display *display,
cristy051718b2011-08-28 22:49:25 +00002893 XResourceInfo *resource_info,XWindows *windows,Image *image,
2894 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00002895{
2896 char
2897 filename[MaxTextExtent];
2898
2899 ImageInfo
2900 *image_info;
2901
2902 MagickStatusType
2903 status;
2904
2905 /*
2906 Request file name from user.
2907 */
2908 if (resource_info->write_filename != (char *) NULL)
2909 (void) CopyMagickString(filename,resource_info->write_filename,
2910 MaxTextExtent);
2911 else
2912 {
2913 char
2914 path[MaxTextExtent];
2915
2916 int
2917 status;
2918
2919 GetPathComponent(image->filename,HeadPath,path);
2920 GetPathComponent(image->filename,TailPath,filename);
2921 status=chdir(path);
2922 if (status == -1)
cristy051718b2011-08-28 22:49:25 +00002923 (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
2924 "UnableToOpenFile","%s",path);
cristy3ed852e2009-09-05 21:47:34 +00002925 }
2926 XFileBrowserWidget(display,windows,"Save",filename);
2927 if (*filename == '\0')
2928 return(MagickTrue);
2929 if (IsPathAccessible(filename) != MagickFalse)
2930 {
2931 int
2932 status;
2933
2934 /*
2935 File exists-- seek user's permission before overwriting.
2936 */
2937 status=XConfirmWidget(display,windows,"Overwrite",filename);
2938 if (status == 0)
2939 return(MagickTrue);
2940 }
2941 image_info=CloneImageInfo(resource_info->image_info);
2942 (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
cristy051718b2011-08-28 22:49:25 +00002943 (void) SetImageInfo(image_info,1,exception);
cristy3ed852e2009-09-05 21:47:34 +00002944 if ((LocaleCompare(image_info->magick,"JPEG") == 0) ||
2945 (LocaleCompare(image_info->magick,"JPG") == 0))
2946 {
2947 char
2948 quality[MaxTextExtent];
2949
2950 int
2951 status;
2952
2953 /*
2954 Request JPEG quality from user.
2955 */
cristyb51dff52011-05-19 16:55:47 +00002956 (void) FormatLocaleString(quality,MaxTextExtent,"%.20g",(double)
cristye8c25f92010-06-03 00:53:06 +00002957 image_info->quality);
cristy3ed852e2009-09-05 21:47:34 +00002958 status=XDialogWidget(display,windows,"Save","Enter JPEG quality:",
2959 quality);
2960 if (*quality == '\0')
2961 return(MagickTrue);
cristye27293e2009-12-18 02:53:20 +00002962 image->quality=StringToUnsignedLong(quality);
cristy3ed852e2009-09-05 21:47:34 +00002963 image_info->interlace=status != MagickFalse ? NoInterlace :
2964 PlaneInterlace;
2965 }
2966 if ((LocaleCompare(image_info->magick,"EPS") == 0) ||
2967 (LocaleCompare(image_info->magick,"PDF") == 0) ||
2968 (LocaleCompare(image_info->magick,"PS") == 0) ||
2969 (LocaleCompare(image_info->magick,"PS2") == 0))
2970 {
2971 char
2972 geometry[MaxTextExtent];
2973
2974 /*
2975 Request page geometry from user.
2976 */
cristy42c2d972009-09-13 01:12:39 +00002977 (void) CopyMagickString(geometry,PSPageGeometry,MaxTextExtent);
cristy3ed852e2009-09-05 21:47:34 +00002978 if (LocaleCompare(image_info->magick,"PDF") == 0)
cristy42c2d972009-09-13 01:12:39 +00002979 (void) CopyMagickString(geometry,PSPageGeometry,MaxTextExtent);
cristy3ed852e2009-09-05 21:47:34 +00002980 if (image_info->page != (char *) NULL)
2981 (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
2982 XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
2983 "Select page geometry:",geometry);
2984 if (*geometry != '\0')
2985 image_info->page=GetPageGeometry(geometry);
2986 }
2987 /*
2988 Write image.
2989 */
2990 image=GetFirstImageInList(image);
cristy051718b2011-08-28 22:49:25 +00002991 status=WriteImages(image_info,image,filename,exception);
cristy3ed852e2009-09-05 21:47:34 +00002992 if (status != MagickFalse)
2993 image->taint=MagickFalse;
2994 image_info=DestroyImageInfo(image_info);
2995 XSetCursorState(display,windows,MagickFalse);
2996 return(status != 0 ? MagickTrue : MagickFalse);
2997}
2998#else
2999
3000/*
3001%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3002% %
3003% %
3004% %
3005+ A n i m a t e I m a g e s %
3006% %
3007% %
3008% %
3009%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3010%
3011% AnimateImages() repeatedly displays an image sequence to any X window
3012% screen. It returns a value other than 0 if successful. Check the
3013% exception member of image to determine the reason for any failure.
3014%
3015% The format of the AnimateImages method is:
3016%
3017% MagickBooleanType AnimateImages(const ImageInfo *image_info,
3018% Image *images)
3019%
3020% A description of each parameter follows:
3021%
3022% o image_info: the image info.
3023%
3024% o image: the image.
3025%
cristy051718b2011-08-28 22:49:25 +00003026% o exception: return any errors or warnings in this structure.
3027%
cristy3ed852e2009-09-05 21:47:34 +00003028*/
3029MagickExport MagickBooleanType AnimateImages(const ImageInfo *image_info,
cristy051718b2011-08-28 22:49:25 +00003030 Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00003031{
3032 assert(image_info != (const ImageInfo *) NULL);
3033 assert(image_info->signature == MagickSignature);
3034 assert(image != (Image *) NULL);
3035 assert(image->signature == MagickSignature);
3036 if (image->debug != MagickFalse)
3037 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristy051718b2011-08-28 22:49:25 +00003038 (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
anthonye5b39652012-04-21 05:37:29 +00003039 "DelegateLibrarySupportNotBuiltIn","'%s' (X11)",image->filename);
cristy3ed852e2009-09-05 21:47:34 +00003040 return(MagickFalse);
3041}
3042#endif