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