1 /*
2  * Copyright (c) 2001 Sasha Vasko <sasha@aftercode.net>
3  * Copyright (c) 2001 Eric Kowalski <eric@beancrock.net>
4  * Copyright (c) 2001 Ethan Fisher <allanon@crystaltokyo.com>
5  *
6  * This module is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19  *
20  */
21 
22 #include "config.h"
23 
24 #define LOCAL_DEBUG
25 
26 #include <ctype.h>
27 #include <errno.h>
28 #include <stdarg.h>
29 #include <math.h>
30 #include <stdio.h>
31 #include <signal.h>
32 #include <stdlib.h>
33 #include <unistd.h>
34 #include <string.h>
35 #if TIME_WITH_SYS_TIME
36 # include <sys/time.h>
37 # include <time.h>
38 #else
39 # if HAVE_SYS_TIME_H
40 #  include <sys/time.h>
41 # else
42 #  include <time.h>
43 # endif
44 #endif
45 
46 
47 #include "../afterbase.h"
48 #include "../afterimage.h"
49 #include "common.h"
50 #if defined(DEBUG_ALLOCS)
51 #undef SHAPE
52 #endif
53 
54 
55 #if !defined(X_DISPLAY_MISSING)
56 int asvisual_empty_XErrorHandler (Display * dpy, XErrorEvent * event);
57 
58 # if defined(SHAPE)
59 #  include <X11/extensions/shape.h>
60 # endif /* SHAPE */
61 #endif /* X_DISPLAY_MISSING */
62 
63 
64 
65 /****h* libAfterImage/ascompose
66  * NAME
67  * ascompose is a tool to compose image(s) and display/save it based on
68  * supplied XML input file.
69  *
70  * SYNOPSIS
71  * ascompose -f file|-s string [-o file] [-t type] [-V]
72  * ascompose -i include_file [-i more_include_file ... ]
73  * 			 -f file|-s string [-o file] [-t type] [-V]
74  * ascompose -f file|-s string [-o file] [-t type] [-V] [-n]
75  * ascompose -f file|-s string [-o file] [-t type [-c compression_level]]
76  * 			 [-V] [-r]
77  * ascompose [-h]
78  * ascompose [-v]
79  *
80  * DESCRIPTION
81  * ascompose reads supplied XML data, and manipulates image accordingly.
82  * It could transform images from files of any supported file format,
83  * draw gradients, render antialiased texturized text, perform
84  * superimposition of arbitrary number of images, and save images into
85  * files of any of supported output file formats.
86  *
87  * At any point, the result of any operation could be assigned a name,
88  * and later on referenced under this name.
89  *
90  * At any point during the script processing, result of any operation
91  * could be saved into a file of any supported file types.
92  *
93  * Internal image format is 32bit ARGB with 8bit per channel.
94  *
95  * Last image referenced, will be displayed in X window, unless -n option
96  * is specified. If -r option is specified, then this image will be
97  * displayed in root window of X display, effectively setting a background
98  * for a desktop. If -o option is specified, this image will also be
99  * saved into the file or requested type.
100  *
101  * ascompose can be compiled to not reference X Window System, thus
102  * allowing it to be used on web servers and any other place. It does not
103  * even require X libraries in that case.
104  *
105  * Supported file types for input are :
106  * XPM   - via internal code, or libXpm library.
107  * JPEG  - via libJpeg library.
108  * PNG   - via libPNG library.
109  * XCF   - via internal code. For now XCF support is not complete as it
110  *         does not merge layers.
111  * PPM/PNM - via internal code.
112  * BMP, ICO, CUR - via internal code.
113  * GIF   - via libungif library.
114  * TIFF  - via libtiff library (including alpha channel support).
115  * see libAfterImage/ASImageFileTypes for more.
116  *
117  * Supported file types for output :
118  * XPM   - via internal code, or libXpm library.
119  * JPEG  - via libJpeg library.
120  * PNG   - via libPNG library.
121  * GIF   - via libungif library.
122  * TIFF  - via libtiff library (including alpha channel support).
123  *
124  * OPTIONS
125  *    -h --help          display help and exit.
126  *    -f --file file     an XML file to use as input.
127  *    -s --string string an XML string to use as input.
128  *    -n --no-display    don't display the last referenced image.
129  *    -r --root-window   draw last referenced image image on root window.
130  *    -o --output file   output last referenced image in to a file.
131  *                       You should use -t to specify what file type to
132  *                       use. Filenames are meaningless when it comes to
133  *                       determining what file type to use.
134  *    -t --type type     type of file to output to.
135  *    -c --compress level compression level.
136  *    -v --version       display version and exit.
137  *    -V --verbose       increase verbosity. To increase verbosity level
138  *                       use several of these, like: ascompose -V -V -V.
139  *    -D --debug         maximum verbosity - show everything and
140  *                       debug messages.
141  *    -i --include file  include file as input prior to processing main
142  * 						 file.
143  * PORTABILITY
144  * ascompose could be used both with and without X window system. It has
145  * been tested on most UNIX flavors on both 32 and 64 bit architecture.
146  * It has also been tested under CYGWIN environment on Windows 95/NT/2000
147  * USES
148  * libAfterImage         all the image manipulation routines.
149  * libAfterBase          Optionally. Misc data handling such as hash
150  *                       tables and console io. Must be used when compiled
151  *                       without X Window support.
152  * libJPEG               JPEG image format support.
153  * libPNG                PNG image format support.
154  * libungif              GIF image format support.
155  * libTIFF               TIFF image format support.
156  * AUTHOR
157  * Ethan Fisher          <allanon at crystaltokyo dot com>
158  * Sasha Vasko           <sasha at aftercode dot net>
159  * Eric Kowalski         <eric at beancrock dot net>
160  *******/
161 
162 
163 ASVisual *asv;
164 int verbose = 0;
165 
version(void)166 void version(void) {
167 	printf("ascompose version 1.2\n");
168 }
169 
usage(void)170 void usage(void) {
171 	fprintf( stdout,
172 		"Usage:\n"
173 		"ascompose [options] [-f file|-|-s string] [-o file]"
174 		"Available options :\n"
175 		"  -h --help          display this help and exit\n"
176         "  -v --version       display version and exit\n"
177 		" Input options : \n"
178 		"  -f --file file     an XML file to use as input\n"
179 		"  					  use '-' for filename to read input from STDIN\n"
180 		"  -s --string string an XML string to use as input\n"
181 		"  -i --include file  process file prior to processing other input\n"
182 		" Output options : \n"
183 #ifndef X_DISPLAY_MISSING
184 		"  -g --geometry WxX+X+Y  set window geometry \n"
185 		"  -T --title  title  set window's title\n"
186 		"     --override      override window Manager's controls \n"
187 		"                     (use for splash windows to avoid window frame)\n"
188 		"     --center        center window on screen\n"
189 		"     --topmost       raise window to the top\n"
190 		"     --no-shape      do not shape window\n"
191 		"  -n --no-display    don't display the final image\n"
192 		"  -r --root-window   draw result image on root window\n"
193 #endif /* X_DISPLAY_MISSING */
194 		"  -o --output file   output to file\n"
195 		"  -t --type type     type of file to output to\n"
196         "  -c --compress level compression level\n"
197 		" Feedback options : \n"
198 		"  -V --verbose       increase verbosity\n"
199 		"  -q --quiet	      output as little information as possible\n"
200 		"  -D --debug         show everything and debug messages\n"
201 		" Interactive options : \n"
202 		"  -I --interactive   run ascompose in interactive mode - tags are processed,\n"
203 		"                     as soon as they are closed.\n"
204 		"     --timeout value time to wait in between displaying images\n"
205 		"     --click-timeout seconds\n"
206 		"                     time to wait for user click before moving on. -1 - forever.\n"
207 		"     --endless       endlessly loop through file or string\n"
208 		"     --dont-clear    don't clear window before displaying next image\n"
209 		" Note that when -I option is used in conjunction with input from\n"
210 		" string or a file - ascompose will endlesly loop through the contents\n"
211 		" untill it is killed - useful for slideshow type of activity.\n"
212 		" When input comes from STDIN, then ascompose will loop untill Ctrl+D\n"
213 		" is received (EOF).\n"
214 		"\n"
215 		"  -C --clipboard     run ascompose waiting for data being copied into clipboard,\n"
216 		"                     and displaying/processing it, if it is xml.\n"
217 		" Examples: \n"
218 		" To display image.jpg on root window : \n"
219 		"   ascompose -r -s \"<img src=image.jpg/>\""
220 		" To display image.jpg on root window scaling it to screen size: \n"
221 		"   ascompose -r -s \"<scale width=$xroot.width height=proportional><img src=image.jpg/></scale>\"\n"
222 	);
223 }
224 
225 /****** libAfterImage/ascompose/sample
226  * EXAMPLE
227  * Here is the default script that gets executed by ascompose, if no
228  * parameters are given :
229  * SOURCE
230  */
231 #if 1
232 static char* default_doc_str = "\
233 <composite op=hue>\
234   <composite op=add>\
235     <scale width=512 height=proportional>\
236 		<img id=rose src=rose512.jpg/></scale>\
237     <tile width=512 height=384><img src=back.xpm/></tile>\
238   </composite>\
239   <tile width=512 height=384><img src=fore.xpm/></tile>\
240 </composite>\
241 <printf format=\"original image width=%d\n\" var=\"rose.width\"/>\
242 <printf format=\"original image height=%d\n\" var=\"rose.height\"/>\
243 <printf format=\
244 \"original image size in pixels=%d\n\" val=$rose.width*$rose.height/>";
245 #else
246 static char* default_doc_str = "\
247   <composite op=add>\
248     <scale width=512 height=proportional>\
249 		<img id=rose src=rose512.jpg/></scale>\
250     <tile width=512 height=384><img src=back.xpm/></tile>\
251   </composite>";
252 #endif
253 /*******/
254 char *load_stdin();
255 
256 typedef struct ASComposeWinProps
257 {
258 	Bool center ;
259 
260 	int geom_x, geom_y ;
261 	unsigned int geom_width, geom_height ;
262 	unsigned long geom_flags ;
263 
264 	Bool override_redirect ;
265 	int timeout ;
266 	int click_timeout ;
267 	Bool on_top ;
268 	const char *title ;
269 	Bool no_shape ;
270 
271 	Bool mapped ;
272 	Bool dont_clear;
273 
274 	int last_x, last_y ;
275 	unsigned int last_width, last_height ;
276 	Pixmap last_root_pmap ;
277 	ASImage *last_root_im ;
278 
279 	Pixmap canvas;
280 	int canvas_width, canvas_height;
281 }ASComposeWinProps;
282 
283 Window showimage(ASImage* im, Bool looping, Window main_window, ASComposeWinProps *props, int dst_x, int dst_y);
284 Window make_main_window(Bool on_root, ASComposeWinProps *props);
285 
286 int screen = 0, depth = 0;
287 Display *dpy = NULL;
288 
main(int argc,char ** argv)289 int main(int argc, char** argv) {
290 
291 	ASImage* im = NULL;
292 	char* doc_str = default_doc_str;
293 	char* doc_file = NULL;
294 	char* doc_save = NULL;
295 	char* doc_save_type = NULL;
296     char *doc_compress = NULL ;
297 	int i;
298 	int display = 1, onroot = 0;
299 	Bool quiet = False ;
300 	enum
301 	{
302 		COMPOSE_Once = 0,
303 		COMPOSE_Interactive,
304 		COMPOSE_XClipboard
305 	}compose_type = COMPOSE_Once ;
306 	Bool endless_loop = False ;
307 	Window main_window = None ;
308 	ASComposeWinProps main_window_props ;
309 
310 	char* ascompose_locale ;
311 	memset(&main_window_props, 0x00, sizeof( main_window_props));
312 	main_window_props.click_timeout = -1;
313 
314 	/* see ASView.1 : */
315 	set_application_name(argv[0]);
316 
317 	ascompose_locale = mystrdup(getenv("LANG"));
318 
319     if( ascompose_locale && strlen(ascompose_locale) > 0)
320     {
321     	as_set_charset( parse_charset_name( ascompose_locale ));
322     }else
323 	{
324 #ifdef I18N
325 		show_warning ("LANG environment variable is not set - use -L \"locale\" command line option to define locale");
326 #endif
327 	}
328 
329 
330 	/* scrap asvisual so we can work on include files ( not displaying anything ) */
331 	asv = create_asvisual(NULL, 0, 32, NULL);
332 
333 	/* Parse command line. */
334 	for (i = 1 ; i < argc ; i++) {
335 		if (!strcmp(argv[i], "--help") || !strcmp(argv[i], "-h")) {
336 			version();
337 			usage();
338 			exit(0);
339 		} else if (!strcmp(argv[i], "--version") || !strcmp(argv[i], "-v")) {
340 			version();
341 			exit(0);
342 		} else if (!strcmp(argv[i], "--quiet") || !strcmp(argv[i], "-q")) {
343 			set_output_threshold(0);
344 			verbose = 0; quiet = True ;
345 		} else if (!strcmp(argv[i], "--verbose") || !strcmp(argv[i], "-V")) {
346 			set_output_threshold(OUTPUT_VERBOSE_THRESHOLD);
347 			verbose++;
348 		} else if (!strcmp(argv[i], "--debug") || !strcmp(argv[i], "-D")) {
349 			set_output_threshold(OUTPUT_LEVEL_DEBUG);
350 			verbose+=2;
351 		} else if ((!strcmp(argv[i], "--file") || !strcmp(argv[i], "-f")) && i < argc + 1) {
352 			doc_file = argv[++i];
353 		} else if ((!strcmp(argv[i], "--include") || !strcmp(argv[i], "-i")) && i < argc + 1)
354 		{
355 			char *incl_str = load_file(argv[++i]);
356 	  		if (!incl_str)
357 			{
358 				fprintf(stderr, "Unable to load file [%s]: %s.\n", argv[i], strerror(errno));
359 			}else
360 			{
361 				ASImage *im = compose_asimage_xml(asv, NULL, NULL, incl_str, ASFLAGS_EVERYTHING, verbose, None, NULL);
362 				free( incl_str );
363 				if( im )
364 					destroy_asimage(&im);
365 			}
366 		} else if ((!strcmp(argv[i], "--string") || !strcmp(argv[i], "-s")) && i < argc + 1) {
367 			doc_str = argv[++i];
368 	   	} else if ((!strcmp(argv[i], "--output") || !strcmp(argv[i], "-o")) && i < argc + 1) {
369 			doc_save = argv[++i];
370 		} else if ((!strcmp(argv[i], "--type") || !strcmp(argv[i], "-t")) && i < argc + 1) {
371 			doc_save_type = argv[++i];
372         } else if ((!strcmp(argv[i], "--compress") || !strcmp(argv[i], "-c")) && i < argc + 1) {
373             doc_compress = argv[++i];
374 		} else if (!strcmp(argv[i], "--interactive") || !strcmp(argv[i], "-I")) {
375             compose_type = COMPOSE_Interactive ;
376 		} else if (strcmp(argv[i], "--click-timeout") == 0 && i < argc + 1) {
377 			main_window_props.click_timeout = strtod( argv[++i ], NULL );
378 		} else if (strcmp(argv[i], "--timeout") == 0 && i < argc + 1) {
379 			main_window_props.timeout = strtod( argv[++i ], NULL );
380 		} else if (!strcmp(argv[i], "--endless")) {
381             endless_loop = True ;
382 		} else if (!strcmp(argv[i], "--dont-clear")) {
383             main_window_props.dont_clear = True ;
384 		}
385 #ifndef X_DISPLAY_MISSING
386 		  else if ((!strcmp(argv[i], "--geometry") || !strcmp(argv[i], "-g")) && i < argc + 1) {
387 			main_window_props.geom_flags = XParseGeometry ( argv[++i],
388 															&main_window_props.geom_x,
389 															&main_window_props.geom_y,
390                       					 					&main_window_props.geom_width,
391 															&main_window_props.geom_height);
392 		} else if (strcmp(argv[i], "--override") == 0 ) {
393 			main_window_props.override_redirect = True;
394 		} else if ((!strcmp(argv[i], "--title") || !strcmp(argv[i], "-T")) && i < argc + 1) {
395 			main_window_props.title = argv[++i];
396 		} else if (strcmp(argv[i], "--center") == 0 ) {
397 			main_window_props.center = True;
398 		} else if (strcmp(argv[i], "--topmost") == 0 ) {
399 			main_window_props.on_top = True;
400 		} else if (strcmp(argv[i], "--no-shape") == 0 ) {
401 			main_window_props.no_shape = True;
402 		} else if (!strcmp(argv[i], "--clipboard") || !strcmp(argv[i], "-C")) {
403 			compose_type = COMPOSE_XClipboard;
404 		}   else if (!strcmp(argv[i], "--no-display") || !strcmp(argv[i], "-n")) {
405 			display = 0;
406 		} else if ((!strcmp(argv[i], "--root-window") || !strcmp(argv[i], "-r")) && i < argc + 1) {
407 			onroot = 1;
408 		}
409 #endif /* X_DISPLAY_MISSING */
410 	}
411 
412 	destroy_asvisual( asv, False );
413 	asv = NULL ;
414 
415 	dpy = NULL ;
416 #ifndef X_DISPLAY_MISSING
417     if( display )
418     {
419 		LOCAL_DEBUG_OUT( "Opening display ...%s", "");
420         dpy = XOpenDisplay(NULL);
421 		LOCAL_DEBUG_OUT( "Done: %p", dpy);
422 		if( dpy )
423 		{
424         	_XA_WM_DELETE_WINDOW = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
425         	screen = DefaultScreen(dpy);
426         	depth = DefaultDepth(dpy, screen);
427 		}
428     }
429 #endif
430 	if( dpy == NULL && doc_file == NULL && doc_str == default_doc_str )
431 		doc_file = strdup("-");
432 
433 	/* Automagically determine the output type, if none was given. */
434 	if (doc_save && !doc_save_type) {
435 		doc_save_type = strrchr(doc_save, '.');
436 		if (doc_save_type) doc_save_type++;
437 	}
438 
439 	LOCAL_DEBUG_OUT( "Creating visual ...%s", "");
440 	asv = create_asvisual(dpy, screen, depth, NULL);
441 	LOCAL_DEBUG_OUT( "Done: %p", asv);
442 
443 	/* Load the document from file, if one was given. */
444 	if( compose_type == COMPOSE_Once )
445 	{
446 		if (doc_file) {
447 			if( strcmp( doc_file, "-") == 0 )
448 				doc_str = load_stdin();
449 			else
450 				doc_str = load_file(doc_file);
451 			if (!doc_str)
452 			{
453 				show_error("Unable to load file [%s]: %s.\n", doc_file, strerror(errno));
454 				exit(1);
455 			}
456 		}
457 
458 		im = compose_asimage_xml(asv, NULL, NULL, doc_str, ASFLAGS_EVERYTHING, verbose, None, NULL);
459 		/* Save the result image if desired. */
460 		if (doc_save && doc_save_type)
461 		{
462         	if(!save_asimage_to_file(doc_save, im, doc_save_type, doc_compress, NULL, 0, 1))
463 				show_error("Save failed.");
464 			else
465 				show_progress("Save successful.");
466 		}
467 		/* Display the image if desired. */
468 		if (display && dpy)
469 		{
470 			showimage(im, False, make_main_window(onroot, &main_window_props), &main_window_props, 0, 0);
471 			LOCAL_DEBUG_OUT( "Image %p displayed", im );
472 		}
473 		/* Done with the image, finally. */
474 		if( im )
475 		{
476 			safe_asimage_destroy( im );
477 			im = NULL ;
478 		}
479 	}else if( compose_type == COMPOSE_Interactive )
480 	{
481 		FILE *fp = stdin ;
482 		int doc_str_len = 0;
483 		if (doc_file && strcmp( doc_file, "-") != 0 )
484 			fp = fopen( doc_file, "rt" );
485 		if( doc_str )
486 			doc_str_len = strlen( doc_str );
487 
488 		if( fp != NULL || doc_str_len > 0 )
489 		{
490 			ASImageManager *my_imman = create_generic_imageman(NULL);
491 			ASFontManager  *my_fontman = create_generic_fontman(asv->dpy, NULL);
492 			int char_count = 0 ;
493 			ASXmlBuffer xb ;
494 
495 			memset( &xb, 0x00, sizeof(xb));
496 
497 			if (display && dpy)
498 				main_window = make_main_window( onroot, &main_window_props );
499 
500 			do
501 			{
502 				reset_xml_buffer( &xb );
503 				if( fp )
504 				{
505 					int c ;
506 					show_progress("Please enter your xml text :" );
507 
508 					while( (c = fgetc(fp)) != EOF )
509 					{
510 						char cc = c;
511 						while( xb.state >= 0 && spool_xml_tag( &xb, &cc, 1 ) <= 0)
512 						{
513 							LOCAL_DEBUG_OUT("[%c] : state=%d, tags_count=%d, level = %d, tag_type = %d",
514 								             cc, xb.state, xb.tags_count, xb.level, xb.tag_type );
515 						}
516 						LOCAL_DEBUG_OUT("[%c] : state=%d, tags_count=%d, level = %d, tag_type = %d",
517 								        cc, xb.state, xb.tags_count, xb.level, xb.tag_type );
518 
519 						++char_count ;
520 						if( ( xb.state == ASXML_Start && xb.tags_count > 0 && xb.level == 0) ||
521 							  xb.state < 0 )
522 							break;
523 					}
524 					if (c == EOF)
525 					{
526 						if (fp != stdin && endless_loop)
527 						{
528 							fseek( fp, 0L, SEEK_SET );
529 							char_count = 0 ;
530 							if( xb.state == ASXML_Start && xb.tags_count == 0 )
531 								continue;
532 						}else if( xb.tags_count == 0 )
533 							break;
534 					}
535 				}else
536 				{
537 					if( char_count >= doc_str_len )
538 					{
539 						if( !endless_loop )
540 							break;
541 						char_count = 0 ;
542 					}
543 					while( char_count < doc_str_len )
544 					{
545 						char_count += spool_xml_tag( &xb, &doc_str[char_count], doc_str_len - char_count );
546 						if( ( xb.state == ASXML_Start && xb.tags_count > 0 && xb.level == 0) ||
547 							  xb.state < 0 )
548 							break;
549 					}
550 				}
551 				if( xb.state == ASXML_Start && xb.tags_count > 0 && xb.level == 0 )
552 				{
553 					int dst_x = 0, dst_y = 0;
554 					xml_elem_t* doc;
555 
556 					if( !display || dpy == NULL || !quiet )
557 						printf("<success tag_count=%d/>\n", xb.tags_count );
558 
559 					add_xml_buffer_chars( &xb, "", 1 );
560 					LOCAL_DEBUG_OUT("buffer: [%s]", xb.buffer );
561 
562 					if ((doc = xml_parse_doc(xb.buffer, NULL)) != NULL)
563 					{
564 						xml_elem_t *tmp, *parm = doc->child?xml_parse_parm(doc->child->parm, NULL):NULL;
565 						im = compose_asimage_xml_from_doc(asv, my_imman, my_fontman, doc, ASFLAGS_EVERYTHING, verbose, None, NULL, -1, -1);
566 
567 						if (parm)
568 						{
569 							for (tmp = parm ; tmp ; tmp = tmp->next)
570 							{
571 								if (!strcmp(tmp->tag, "x")) dst_x = parse_math(tmp->parm, NULL, 0);
572 								else if (!strcmp(tmp->tag, "y")) dst_y = parse_math(tmp->parm, NULL, 0);
573 							}
574 							xml_elem_delete(NULL, parm);
575 						}
576 						xml_elem_delete(NULL, doc);
577 					}
578 					if( im )
579 					{
580 						/* Save the result image if desired. */
581 						if (doc_save && doc_save_type)
582 						{
583         					if(!save_asimage_to_file(doc_save, im, doc_save_type, doc_compress, NULL, 0, 1))
584 								show_error("Save failed.");
585 							else
586 								show_progress("Save successful.");
587 						}
588 						/* Display the image if desired. */
589 						if (display && dpy)
590 							main_window = showimage(im, True, main_window, &main_window_props, dst_x, dst_y);
591 
592 						safe_asimage_destroy(im);
593 						im = NULL ;
594 					}
595 				}else if( fp == stdin && xb.state == ASXML_Start && xb.tags_count == 0 && xb.level == 0 )
596 				{
597 					if( !display || dpy == NULL || !quiet )
598 						printf("<success tag_count=%d/>\n", xb.tags_count );
599 					if (!endless_loop)
600 						break;
601 				}else
602 				{
603 					if( !display || dpy == NULL || !quiet )
604 					{
605 						xml_elem_t *msg = format_xml_buffer_state (&xb);
606 						if (msg)
607 						{
608 							xml_print(msg);
609 							xml_elem_delete(NULL, msg);
610 						}
611 					}
612 					if (!endless_loop)
613 						break;
614 				}
615 			}while( !display || dpy == NULL || main_window != None);
616 			if( xb.buffer )
617 				free( xb.buffer );
618 			destroy_image_manager(my_imman, False);
619 			destroy_font_manager(my_fontman, False);
620 		}
621 		if( fp && fp != stdin )
622 			fclose( fp );
623 	}
624 #ifndef X_DISPLAY_MISSING
625 	else if( compose_type == COMPOSE_XClipboard && dpy )
626 	{
627 		Atom clipboard_prop ;
628 		ASXmlBuffer xb ;
629 		int nbytes = 0 ;
630 		char *bytes = NULL ;
631 		int char_count = 0 ;
632 		ASImageManager *my_imman = create_generic_imageman(NULL);
633 		ASFontManager  *my_fontman = create_generic_fontman(asv->dpy, NULL);
634 
635 		memset( &xb, 0x00, sizeof(xb));
636 		if (display)
637 			main_window = make_main_window( onroot, &main_window_props );
638 
639 		XSelectInput( dpy, DefaultRootWindow(dpy), PropertyChangeMask );
640 		clipboard_prop = XInternAtom( dpy, "CUT_BUFFER0", False );
641 		while( main_window || !display )
642 		{
643     		XEvent event ;
644 			Bool show_next = False ;
645 
646 			XNextEvent (dpy, &event);
647   			switch(event.type)
648 			{
649 				case PropertyNotify :
650 					if( event.xproperty.atom == clipboard_prop )
651 					{
652 						if( bytes )
653 							XFree(bytes);
654 						bytes = XFetchBytes( dpy, &nbytes );
655 						char_count = 0 ;
656 						show_next = True ;
657 					}
658 				    break ;
659 	  			case ClientMessage:
660 					if (event.xclient.format == 32 &&
661 	  					event.xclient.data.l[0] == _XA_WM_DELETE_WINDOW)
662 					{
663 						if( main_window != DefaultRootWindow(dpy) )
664 							XDestroyWindow( dpy, main_window );
665 						XFlush( dpy );
666 						main_window = None ;
667 					}
668 					break;
669 				case ButtonPress:
670 					if( nbytes > char_count )
671 						show_next = True ;
672 					else if( main_window != DefaultRootWindow(dpy) )
673 						XUnmapWindow( dpy, main_window );
674 					break;
675 			}
676 			if( show_next )
677 			{
678 				reset_xml_buffer( &xb );
679 				while( char_count < nbytes )
680 				{
681 					char_count += spool_xml_tag( &xb, &bytes[char_count], nbytes - char_count );
682 					if( ( xb.state == ASXML_Start && xb.tags_count > 0 && xb.level == 0) ||
683 						xb.state < 0 )
684 						break;
685 				}
686 
687 				if( xb.state == ASXML_Start && xb.tags_count > 0 && xb.level == 0 )
688 				{
689 					xml_elem_t* doc;
690 					int dst_x = 0, dst_y = 0;
691 
692 					add_xml_buffer_chars( &xb, "", 1 );
693 					LOCAL_DEBUG_OUT("buffer: [%s]", xb.buffer );
694 
695 					if ((doc = xml_parse_doc(xb.buffer, NULL)) != NULL)
696 					{
697 						xml_elem_t *tmp, *parm = doc->child?xml_parse_parm(doc->child->parm, NULL):NULL;
698 						im = compose_asimage_xml_from_doc(asv, my_imman, my_fontman, doc, ASFLAGS_EVERYTHING, verbose, None, NULL, -1, -1);
699 						if (parm)
700 						{
701 							for (tmp = parm ; tmp ; tmp = tmp->next)
702 							{
703 								if (!strcmp(tmp->tag, "x")) dst_x = parse_math(tmp->parm, NULL, 0);
704 								else if (!strcmp(tmp->tag, "y")) dst_y = parse_math(tmp->parm, NULL, 0);
705 							}
706 							xml_elem_delete(NULL, parm);
707 						}
708 						xml_elem_delete(NULL, doc);
709 					}
710 
711 					if( im )
712 					{
713 						/* Save the result image if desired. */
714 						if (doc_save && doc_save_type)
715 						{
716         					if(!save_asimage_to_file(doc_save, im, doc_save_type, doc_compress, NULL, 0, 1))
717 								show_error("Save failed.");
718 							else
719 								show_progress("Save successful.");
720 						}
721 						/* Display the image if desired. */
722 						if (display && dpy)
723 							main_window = showimage(im, True, main_window, &main_window_props, dst_x, dst_y);
724 						safe_asimage_destroy(im);
725 						im = NULL ;
726 					}
727 				}
728 			}
729 		}
730 
731 		if( bytes )
732 			XFree( bytes );
733 		if( xb.buffer )
734 			free( xb.buffer );
735 		destroy_image_manager(my_imman, False);
736 		destroy_font_manager(my_fontman, False);
737 	}
738 #endif
739 	if (doc_file && doc_str && doc_str != default_doc_str) free(doc_str);
740 
741 #if !defined(X_DISPLAY_MISSING)
742 	if (main_window_props.canvas)
743 		XFreePixmap(dpy, main_window_props.canvas);
744 	if( dpy )
745 	{
746         XCloseDisplay (dpy);
747 		dpy = NULL;
748 	}
749 #endif
750 	LOCAL_DEBUG_OUT( "display Closed%s","");
751 #ifdef DEBUG_ALLOCS
752 	if (main_window_props.last_root_im)
753 		safe_asimage_destroy(main_window_props.last_root_im);
754 	asxml_var_cleanup();
755 	LOCAL_DEBUG_OUT( "display Closed%s","");
756 	custom_color_cleanup();
757 	LOCAL_DEBUG_OUT( "display Closed%s","");
758     build_xpm_colormap( NULL );
759 	LOCAL_DEBUG_OUT( "display Closed%s","");
760 	flush_default_asstorage();
761 	LOCAL_DEBUG_OUT( "display Closed%s","");
762 //	destroy_asvisual( asv, False );
763 	LOCAL_DEBUG_OUT( "display Closed%s","");
764     flush_ashash_memory_pool();
765 	LOCAL_DEBUG_OUT( "display Closed%s","");
766 	print_unfreed_mem();
767 	print_asimage_registry();
768 #endif
769 
770 	return 0;
771 }
772 
773 Window
make_main_window(Bool onroot,ASComposeWinProps * props)774 make_main_window(Bool onroot, ASComposeWinProps *props)
775 {
776 	Window w = None ;
777 #ifndef X_DISPLAY_MISSING
778 	XSetWindowAttributes attributes;
779 
780 	if( onroot )
781 	{
782 		w = DefaultRootWindow(dpy);
783 		props->last_x = 0 ;
784 		props->last_y = 0 ;
785 		props->last_width = 0 ;
786 		props->last_height = 0 ;
787 	}else
788 	{
789 		attributes.override_redirect = props->override_redirect ;
790 		w = create_top_level_window( asv, DefaultRootWindow(dpy), 32, 32,
791 				                        100, 30, 0, CWOverrideRedirect, &attributes,
792 										"ASCompose",
793 										props->title );
794 		props->last_x = 32 ;
795 		props->last_y = 32 ;
796 		props->last_width = 100 ;
797 		props->last_height = 30 ;
798 		XSelectInput (dpy, w, (StructureNotifyMask|ButtonPressMask|ButtonReleaseMask));
799 	}
800 	props->last_root_pmap = None ;
801 	if( props->last_root_im )
802 		safe_asimage_destroy(props->last_root_im);
803 	props->last_root_im = NULL ;
804 
805 
806 #endif
807 	return w;
808 }
809 
810 Bool
set_root_pixmap_property(long pmap)811 set_root_pixmap_property(long pmap) /* Must have long type to work with XChangeProp on 64 bit machines !!!*/
812 {
813 #ifndef X_DISPLAY_MISSING
814 	Window root = DefaultRootWindow(dpy);
815 	char  *names[2] = {"_XROOTPMAP_ID", "ESETROOT_PMAP_ID"};
816 	Atom  atoms[2];
817 	int i;
818 
819 	if (XInternAtoms (dpy, &(names[0]), 2, True, &(atoms[0])) != 0)
820 	{
821 		Pixmap pmaps[2] = {0, 0};
822 	    Atom type;
823     	int format;
824 	    unsigned long nitems, after;
825 		union
826 		{
827 			unsigned char *uc_ptr ;
828 			long 		  *long_ptr ;
829 		}data;
830 
831 		for (i = 0 ; i < 2 ; ++i)
832 			if (atoms[i])
833 			{
834 				LOCAL_DEBUG_OUT("atoms[%d] = %lX", i, atoms[i]);
835 				data.long_ptr = NULL ;
836 		        XGetWindowProperty(dpy, root, atoms[i], 0L, 1L, False, AnyPropertyType, &type, &format, &nitems, &after, &data.uc_ptr);
837 				if (data.long_ptr == NULL)
838 					break;
839 				pmaps[i] = data.long_ptr[0] ;
840 				LOCAL_DEBUG_OUT("pmaps[%d] = %lX", i, pmaps[i]);
841 				if (format != 32 || nitems == 0 || pmaps[i] != pmaps[0] || type != XA_PIXMAP)
842 					break;
843 			}
844 		if (i>= 2 && pmaps[0] && pmaps[0] != pmap)
845 		{
846 			/* XKillClient is a dangerous affair since properties may hold stale values tha are no longer valid */
847 			int (*oldXErrorHandler) (Display *, XErrorEvent *) = XSetErrorHandler (asvisual_empty_XErrorHandler);
848 			LOCAL_DEBUG_OUT("killing client for pmap %lX", pmaps[0]);
849            	XKillClient(dpy, pmaps[0]);
850 			XSync(dpy, False);
851 			XSetErrorHandler (oldXErrorHandler);
852 		}
853     }
854 	for (i = 0 ; i < 2 ; ++i)
855 	{
856 		if (!atoms[i])
857 			if ( (atoms[i] = XInternAtom (dpy, names[i], False)) == None)
858 				break;
859 		LOCAL_DEBUG_OUT("Changing property %lX to pmap id %lX", atoms[i], pmap);
860     	XChangeProperty(dpy, root, atoms[i], XA_PIXMAP, 32, PropModeReplace, (unsigned char *) &pmap, 1);
861 	}
862 	if (i >= 2)
863 	{
864 		XSetCloseDownMode(dpy, RetainPermanent);
865 		return True;
866 	}
867 #endif
868 	return False;
869 }
870 
871 Bool
wait_x_timeout(int timeout)872 wait_x_timeout(int timeout)
873 {
874 #ifndef X_DISPLAY_MISSING
875 	if (timeout < 0) /* wait forever */
876 	{
877 		XEvent evt;
878 		XPeekEvent(dpy, &evt);
879 		return True;
880 	}else if (timeout > 0)
881 	{
882 		fd_set        in_fdset;
883 		int x_fd = XConnectionNumber (dpy);
884 		struct timeval tv;
885 		struct timeval *t = NULL;
886 		int retval = 0 ;
887 
888 		FD_ZERO (&in_fdset);
889 		FD_SET (x_fd, &in_fdset);
890 		tv.tv_sec = timeout/1000 ;
891 		tv.tv_usec = (timeout%1000)*1000;
892 		t = &tv;
893 		retval = PORTABLE_SELECT(x_fd+1,&in_fdset,NULL,NULL,t);
894 		return (retval > 0);
895 	}
896 
897 	return XPending(dpy);
898 #endif
899 	return 0;
900 }
901 
showimage(ASImage * im,Bool looping,Window main_window,ASComposeWinProps * props,int dst_x,int dst_y)902 Window showimage(ASImage* im, Bool looping, Window main_window, ASComposeWinProps *props, int dst_x, int dst_y )
903 {
904 #ifndef X_DISPLAY_MISSING
905 	int x = 32, y = 32;
906 	ASImage *orig_im = im ;
907 	unsigned int width, height;
908 	unsigned int shape_rects_count = 0;
909 	XRectangle *shape_rects = NULL ;
910 	Bool done = False ;
911 	Window root = DefaultRootWindow(dpy);
912 	int screen = DefaultScreen(dpy);
913 	int root_w = DisplayWidth (dpy, screen);
914 	int root_h = DisplayHeight (dpy, screen);
915 	Pixmap saved_canvas = None;
916 
917 	if (im == NULL || main_window == None )
918 		return None;
919 
920 	width = im->width + dst_x;
921 	height = im->height + dst_y;
922 
923 	if( main_window != root)
924 	{
925 		Bool move = True ;
926 
927 		if( get_flags( props->geom_flags, WidthValue) && props->geom_width > 0  )
928 			width = props->geom_width ;
929 		if( get_flags( props->geom_flags, HeightValue)&& props->geom_height > 0  )
930 			height = props->geom_height ;
931 
932 		if( props->center )
933 		{
934 			x = (root_w - width)/2;
935 			y = (root_h - height)/2;
936 		}else if( get_flags( props->geom_flags, XValue|YValue) )
937 		{
938 	 		x = props->geom_x ;
939 			y = props->geom_y ;
940 			if( get_flags( props->geom_flags, XNegative ) )
941 				x = root_w - width + x ;
942 			if( get_flags( props->geom_flags, YNegative ) )
943 				y = root_h - height + y ;
944 		}else
945 			move = False ;
946 
947 		if( move && (props->last_x != x || props->last_y != y))
948 		{
949 			XMoveWindow( dpy, main_window, x, y );
950 			props->last_x = x ;
951 			props->last_y = y ;
952 		}
953 		if( props->last_width != width || props->last_height != height )
954 		{
955 			XResizeWindow( dpy, main_window, width, height );
956 			props->last_width = width ;
957 			props->last_height = height ;
958 			saved_canvas = props->canvas;
959 			props->canvas = None; /* force resizng the pixmap !!! */
960 		}
961 
962 		if( !props->mapped )
963 		{
964 			if( props->geom_flags != 0 )
965 			{
966 				XSizeHints hints ;
967 				hints.flags = PWinGravity ;
968 				if( get_flags( props->geom_flags, WidthValue|HeightValue) )
969 					hints.flags |= USSize ;
970 				if( get_flags( props->geom_flags, XValue|YValue) )
971 					hints.flags |= USPosition ;
972 				hints.win_gravity = NorthWestGravity ;
973 				if( get_flags( props->geom_flags, XNegative) && !get_flags( props->geom_flags, YNegative)  )
974 					hints.win_gravity = NorthEastGravity ;
975 				else if( !get_flags( props->geom_flags, XNegative) && get_flags( props->geom_flags, YNegative)  )
976 					hints.win_gravity = SouthWestGravity ;
977 				else if( get_flags( props->geom_flags, XNegative) && get_flags( props->geom_flags, YNegative)  )
978 					hints.win_gravity = SouthEastGravity ;
979 				XSetWMNormalHints( dpy, main_window, &hints );
980 			}
981 			XMapWindow( dpy, main_window);
982 			props->mapped = True ;
983 		}
984 		if( props->on_top )
985 			XRaiseWindow( dpy, main_window );
986 		if( get_flags(get_asimage_chanmask(im), SCL_DO_ALPHA))
987 		{
988 #ifdef SHAPE
989 			if( !props->no_shape )
990 				shape_rects = get_asimage_channel_rects( im, IC_ALPHA, 10, &shape_rects_count );
991 #endif
992 #if 1
993 			{
994 				unsigned int root_pmap_width, root_pmap_height;
995 				Pixmap rp = GetRootPixmap(None);
996 				ASImage *transp_im = NULL , *tmp ;
997 				int root_x, root_y ; Window wdumm;
998 				XTranslateCoordinates( dpy, main_window, root, 0, 0, &root_x, &root_y, &wdumm);
999 
1000 				if (rp)
1001 				{
1002 					if( props->last_root_pmap != rp ||
1003 						props->last_root_im == NULL )
1004 					{
1005 						if( props->last_root_im )
1006 							safe_asimage_destroy(props->last_root_im);
1007 						get_dpy_drawable_size(asv->dpy, rp, &root_pmap_width, &root_pmap_height);
1008 						transp_im = pixmap2asimage(asv, rp, 0, 0, root_pmap_width, root_pmap_height, 0xFFFFFFFF, False, 0);
1009 						props->last_root_pmap = rp ;
1010 						props->last_root_im = transp_im ;
1011 					}else
1012 					{
1013 						width = props->last_root_im->width ;
1014 						height = props->last_root_im->height ;
1015 						transp_im = props->last_root_im ;
1016 					}
1017 				}
1018 
1019 				if( transp_im )
1020 				{   /* Build the layers first. */
1021 					ASImageLayer *layers = create_image_layers( 2 );
1022 
1023 					layers[0].im = transp_im ;
1024 					layers[0].clip_x = root_x+dst_x;
1025 					layers[0].clip_y = root_y+dst_y;
1026 					layers[0].clip_width = im->width ;
1027 					layers[0].clip_height = im->height ;
1028 					layers[1].im = im ;
1029 					layers[1].clip_width = im->width ;
1030 					layers[1].clip_height = im->height ;
1031 					tmp = merge_layers(asv, layers, 2, im->width, im->height,
1032 									   get_flags( asv->glx_support, ASGLX_UseForImageTx )?ASA_ASImage:ASA_XImage, 0, ASIMAGE_QUALITY_DEFAULT);
1033 					if( tmp )
1034 						im = tmp ;
1035 					free( layers );
1036 				}
1037 			}
1038 #endif
1039 		}
1040 	}
1041 
1042 	LOCAL_DEBUG_OUT("canvas pixmap set to %lX", props->canvas);
1043 
1044 	if (props->canvas == None)
1045 	{
1046 		int pmap_width = width;
1047 		int pmap_height = height;
1048 		GC gc = NULL;
1049 		if (main_window==root)
1050 		{
1051 			pmap_width = dst_x+im->width;
1052 			pmap_height = dst_x+im->height;
1053 			depth = DefaultDepth(dpy,screen);
1054 			gc = DefaultGC(dpy, screen);
1055 		}
1056 		props->canvas = create_visual_pixmap( asv, main_window, pmap_width, pmap_height, DefaultDepth(dpy,screen));
1057 		LOCAL_DEBUG_OUT("Created new canvas pixmap %lX", props->canvas);
1058 
1059 		if (gc == NULL)
1060 			gc = XCreateGC( dpy, main_window, 0, NULL);
1061 
1062 		if (pmap_width != im->width || pmap_height != im->height)
1063 			XFillRectangle( dpy, props->canvas, gc, 0, 0, pmap_width, pmap_height);
1064 
1065 		if (saved_canvas)
1066             XCopyArea (dpy, saved_canvas, props->canvas, gc,
1067 			           0, 0, MIN(props->canvas_width,pmap_width), MIN(props->canvas_height,pmap_height),
1068 					   0, 0);
1069 
1070 		if (gc != DefaultGC(dpy, screen))
1071 			XFreeGC (dpy, gc);
1072 
1073 		props->canvas_width = pmap_width;
1074 		props->canvas_height = pmap_height;
1075 	}
1076 	if (main_window==root)
1077 	{
1078 		XImage *xim = create_visual_scratch_ximage( asv, im->width, im->height, DefaultDepth(dpy,screen) );
1079 		if( subimage2ximage (asv, im, 0, 0, xim)	)
1080 		{
1081 			put_ximage( asv, xim, props->canvas, DefaultGC(dpy,screen), 0, 0, dst_x, dst_y, im->width, im->height );
1082 		}
1083 		XDestroyImage( xim );
1084 	}else
1085 	{
1086 		if( get_flags( asv->glx_support, ASGLX_UseForImageTx ) )
1087 			done = asimage2drawable_gl( asv, props->canvas, im, 0, 0, 0, 0,
1088 										im->width, im->height,
1089 				   						im->width, im->height,
1090 										False );
1091 		if( !done )
1092 			asimage2drawable( asv, props->canvas, im, NULL, 0, 0, dst_x, dst_y, im->width, im->height, True);
1093 	}
1094 
1095 	if( im != orig_im )
1096 	{
1097 		safe_asimage_destroy(im);
1098 		im = orig_im ;
1099 	}
1100 
1101 	XSetWindowBackgroundPixmap( dpy, main_window, props->canvas);
1102 	XClearWindow( dpy, main_window);
1103 	XFlush(dpy);
1104 	if (main_window == root && !looping)
1105 	{
1106 		if (set_root_pixmap_property(props->canvas))
1107 		{
1108 			props->canvas = None;
1109 			XSync(dpy,False);
1110 		}
1111 	}
1112 
1113 	if (saved_canvas)
1114 	{
1115 		LOCAL_DEBUG_OUT("Freeing saved canvas pixmap %lX", saved_canvas);
1116 		XFreePixmap( dpy, saved_canvas);
1117 	}
1118 
1119 	if (main_window == root && !looping)
1120 		return root;
1121 
1122 #if 1
1123 #ifdef SHAPE
1124 	if( shape_rects == NULL || shape_rects_count == 0 )
1125 		XShapeCombineMask( dpy, main_window, ShapeBounding, 0, 0, None, ShapeSet );
1126 	else
1127 	{
1128 		XShapeCombineRectangles (dpy, main_window, ShapeBounding, 0, 0, shape_rects, shape_rects_count, ShapeUnion, Unsorted);
1129 		free( shape_rects );
1130 		shape_rects = NULL ;
1131 	}
1132 #endif
1133 #endif
1134 	XSync(dpy, False);
1135 
1136 	while(main_window != None)
1137   	{
1138     	XEvent event ;
1139 		Bool do_close = False ;
1140 
1141 		if ( props->timeout && !wait_x_timeout(props->timeout) )
1142 		{
1143 			if( looping )
1144 				return main_window;
1145 			do_close = True ;
1146 		}
1147 		if( !do_close )
1148 		{
1149 			if (!XPending(dpy))
1150 				if (!wait_x_timeout(props->click_timeout))
1151 					if( looping )
1152 						return main_window;
1153 
1154 	    	XNextEvent (dpy, &event);
1155   			switch(event.type)
1156 			{
1157 	  			case ClientMessage:
1158 			    	if (event.xclient.format == 32 &&
1159 	  			    	event.xclient.data.l[0] == _XA_WM_DELETE_WINDOW)
1160 					{
1161 						do_close = True ;
1162 					}
1163 					break;
1164 		  		case ButtonPress:
1165 					LOCAL_DEBUG_OUT( "ButtonPress: looping = %d", looping);
1166 					if( looping )
1167 						return main_window;
1168 					do_close = True ;
1169 					break;
1170 			}
1171 		}
1172 		if( do_close )
1173 		{
1174 			if( main_window != root )
1175 				XDestroyWindow( dpy, main_window );
1176 			XFlush( dpy );
1177 			main_window = None ;
1178 		}
1179   	}
1180 
1181 #endif /* X_DISPLAY_MISSING */
1182 	return main_window;
1183 }
1184 
1185 
load_stdin()1186 char *load_stdin()
1187 {
1188 #define BUFSIZE 512
1189 	char buffer[BUFSIZE] ;
1190 	char *complete = safemalloc(8192) ;
1191 	int complete_allocated = 8192 ;
1192 	int complete_curr = 0 ;
1193 	int len ;
1194 
1195 	while( fgets( &buffer[0], BUFSIZE, stdin ) != NULL )
1196 	{
1197 		len = strlen( &buffer[0] );
1198 		if( complete_curr + len > complete_allocated )
1199 		{
1200 			complete_allocated+=len	  ;
1201 	 		complete = realloc( complete, complete_allocated );
1202 		}
1203 		memcpy( &complete[complete_curr], &buffer[0], len );
1204 		complete_curr += len ;
1205 	}
1206 	return complete;
1207 }
1208 
1209