1 /*
2  * x11.c
3  * kirk johnson
4  * july 1993
5  *
6  * 27-SEP-2012 modified by Anton Shterenlikht
7  *
8  * Copyright (C) 1989, 1990, 1993-1995, 1999 Kirk Lauritz Johnson
9  *
10  * Parts of the source code (as marked) are:
11  *   Copyright (C) 1989, 1990, 1991 by Jim Frost
12  *   Copyright (C) 1992 by Jamie Zawinski <jwz@lucid.com>
13  *
14  * Permission to use, copy, modify and freely distribute xearth for
15  * non-commercial and not-for-profit purposes is hereby granted
16  * without fee, provided that both the above copyright notice and this
17  * permission notice appear in all copies and in supporting
18  * documentation.
19  *
20  * Unisys Corporation holds worldwide patent rights on the Lempel Zev
21  * Welch (LZW) compression technique employed in the CompuServe GIF
22  * image file format as well as in other formats. Unisys has made it
23  * clear, however, that it does not require licensing or fees to be
24  * paid for freely distributed, non-commercial applications (such as
25  * xearth) that employ LZW/GIF technology. Those wishing further
26  * information about licensing the LZW patent should contact Unisys
27  * directly at (lzw_info@unisys.com) or by writing to
28  *
29  *   Unisys Corporation
30  *   Welch Licensing Department
31  *   M/S-C1SW19
32  *   P.O. Box 500
33  *   Blue Bell, PA 19424
34  *
35  * The author makes no representations about the suitability of this
36  * software for any purpose. It is provided "as is" without express or
37  * implied warranty.
38  *
39  * THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
40  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
41  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT
42  * OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
43  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
44  * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
45  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
46  */
47 
48 #include "xearth.h"
49 #include <X11/Xlib.h>
50 #include <X11/Intrinsic.h>
51 #include <X11/Xatom.h>
52 #include <X11/Xproto.h>
53 #include <signal.h>
54 #include "kljcpyrt.h"
55 
56 #define RETAIN_PROP_NAME "_XSETROOT_ID"
57 
58 #define MONO_1   (0)
59 #define MONO_8   (1)
60 #define COLOR_8  (2)
61 #define MONO_16  (3)
62 #define COLOR_16 (4)
63 #define MONO_32  (5)
64 #define COLOR_32 (6)
65 
66 #define LABEL_LEFT_FLUSH (1<<0)
67 #define LABEL_TOP_FLUSH  (1<<1)
68 
69 static void         init_x_general _P((int, char *[]));
70 static void         process_opts _P((void));
71 static void         init_x_colors _P((void));
72 static void         init_x_pixmaps _P((void));
73 static void         init_x_root_window _P((void));
74 static void         init_x_separate_window _P((void));
75 static void         wakeup _P((int));
76 static int          get_bits_per_pixel _P((int));
77 static XFontStruct *load_x_font _P((Display *, char *));
78 static void         get_proj_type _P((void));
79 static void         get_viewing_position _P((void));
80 static void         get_sun_position _P((void));
81 static void         get_rotation _P((void));
82 static void         get_size _P((void));
83 static void         get_shift _P((void));
84 static void         get_labelpos _P((void));
85 static void         get_geometry _P((void));
86 static void         x11_setup _P((void));
87 static void         pack_mono_1 _P((u16or32 *, u_char *));
88 static void         pack_8 _P((u16or32 *, Pixel *, u_char *));
89 static void         pack_16 _P((u16or32 *, Pixel *, u_char *));
90 static void         pack_24 _P((u16or32 *, Pixel *, u_char *));
91 static void         pack_32 _P((u16or32 *, Pixel *, u_char *));
92 static int          x11_row _P((u_char *));
93 static void         x11_cleanup _P((void));
94 static void         draw_label _P((Display *));
95 static void         mark_location _P((Display *, MarkerInfo *));
96 static void         draw_outlined_string _P((Display *, Pixmap, Pixel, Pixel,
97                       int, int, char *, int));
98 static Window       GetVRoot _P((Display *));
99 static void         updateProperty _P((Display *, Window, const char *, Atom,
100                       int, int, int));
101 static void         preserveResource _P((Display *, Window));
102 static void         freePrevious _P((Display *, Window));
103 static int          xkill_handler _P((Display *, XErrorEvent *));
104 
105 static int      bpp;
106 static u16or32 *dith;
107 static u_char  *xbuf;
108 static int      idx;
109 static XImage  *xim;
110 static Pixmap   work_pix;
111 static Pixmap   disp_pix;
112 static int    (*orig_error_handler) _P((Display *, XErrorEvent *));
113 
114 #ifdef DEBUG
115 static int frame = 0;
116 #endif /* DEBUG */
117 
118 char        *progclass;
119 Widget       app_shell;
120 XtAppContext app_context;
121 XrmDatabase  db;
122 
123 Display     *dsply;             /* display connection  */
124 int          scrn;              /* screen number       */
125 Window       root;              /* root window         */
126 Window       xearth_window;     /* xearth window       */
127 Colormap     cmap;              /* default colormap    */
128 Visual      *visl;              /* default visual      */
129 int          dpth;              /* default depth       */
130 Pixel        white;             /* white pixel         */
131 Pixel        black;             /* black pixel         */
132 Pixel        hlight;            /* highlight pixel     */
133 GC           gc;                /* graphics context    */
134 Pixel       *pels;              /* allocated colors    */
135 char        *font_name;         /* text font name      */
136 XFontStruct *font;              /* basic text font     */
137 
138 static int   do_once;           /* only render once?   */
139 static int   mono;              /* render in mono?     */
140 static int   use_root;          /* render into root?   */
141 static int   window_pos_flag;   /* spec'ed window pos? */
142 static int   window_xvalue;     /* window x position   */
143 static int   window_yvalue;     /* window y position   */
144 static int   window_gravity;    /* window gravity      */
145 
146 static int   label_xvalue;      /* label x position    */
147 static int   label_yvalue;      /* label y position    */
148 static int   label_orient;      /* label orientation   */
149 
150 
151 static char *defaults[] =
152 {
153   "*proj:       orthographic",
154   "*pos:        sunrel 0 0",
155   "*rot:        0",
156   "*shift:      0 0",
157   "*mag:        1.0",
158   "*shade:      on",
159   "*label:      off",
160   "*labelpos:   -5-5",
161   "*markers:    on",
162   "*markerfile: built-in",
163   "*wait:       300",
164   "*timewarp:   1",
165   "*day:        100",
166   "*night:      5",
167   "*term:       1",
168   "*twopix:     on",
169   "*ncolors:    64",
170   "*fork:       off",
171   "*once:       off",
172   "*nice:       0",
173   "*stars:      on",
174   "*starfreq:   0.002",
175   "*bigstars:   0",
176   "*grid:       off",
177   "*grid1:      6",
178   "*grid2:      15",
179   "*gamma:      1.0",
180   "*font:       variable",
181   "*title:      xearth",
182   "*iconname:   xearth",
183   NULL
184 };
185 
186 static XrmOptionDescRec options[] =
187 {
188 { "-proj",        ".proj",        XrmoptionSepArg, 0     },
189 { "-pos",         ".pos",         XrmoptionSepArg, 0     },
190 { "-rot",         ".rot",         XrmoptionSepArg, 0     },
191 { "-mag",         ".mag",         XrmoptionSepArg, 0     },
192 { "-shade",       ".shade",       XrmoptionNoArg,  "on"  },
193 { "-noshade",     ".shade",       XrmoptionNoArg,  "off" },
194 { "-sunpos",      ".sunpos",      XrmoptionSepArg, 0     },
195 { "-size",        ".size",        XrmoptionSepArg, 0     },
196 { "-shift",       ".shift",       XrmoptionSepArg, 0     },
197 { "-label",       ".label",       XrmoptionNoArg,  "on"  },
198 { "-nolabel",     ".label",       XrmoptionNoArg,  "off" },
199 { "-labelpos",    ".labelpos",    XrmoptionSepArg, 0     },
200 { "-markers",     ".markers",     XrmoptionNoArg,  "on"  },
201 { "-nomarkers",   ".markers",     XrmoptionNoArg,  "off" },
202 { "-markerfile",  ".markerfile",  XrmoptionSepArg, 0     },
203 { "-showmarkers", ".showmarkers", XrmoptionNoArg,  "on"  },
204 { "-wait",        ".wait",        XrmoptionSepArg, 0     },
205 { "-timewarp",    ".timewarp",    XrmoptionSepArg, 0     },
206 { "-day",         ".day",         XrmoptionSepArg, 0     },
207 { "-night",       ".night",       XrmoptionSepArg, 0     },
208 { "-term",        ".term",        XrmoptionSepArg, 0     },
209 { "-onepix",      ".twopix",      XrmoptionNoArg,  "off" },
210 { "-twopix",      ".twopix",      XrmoptionNoArg,  "on"  },
211 { "-ncolors",     ".ncolors",     XrmoptionSepArg, 0     },
212 { "-fork",        ".fork",        XrmoptionNoArg,  "on"  },
213 { "-nofork",      ".fork",        XrmoptionNoArg,  "off" },
214 { "-once",        ".once",        XrmoptionNoArg,  "on"  },
215 { "-noonce",      ".once",        XrmoptionNoArg,  "off" },
216 { "-nice",        ".nice",        XrmoptionSepArg, 0     },
217 { "-version",     ".version",     XrmoptionNoArg,  "on"  },
218 { "-stars",       ".stars",       XrmoptionNoArg,  "on"  },
219 { "-nostars",     ".stars",       XrmoptionNoArg,  "off" },
220 { "-starfreq",    ".starfreq",    XrmoptionSepArg, 0     },
221 { "-bigstars",    ".bigstars",    XrmoptionSepArg, 0     },
222 { "-grid",        ".grid",        XrmoptionNoArg,  "on"  },
223 { "-nogrid",      ".grid",        XrmoptionNoArg,  "off" },
224 { "-grid1",       ".grid1",       XrmoptionSepArg, 0     },
225 { "-grid2",       ".grid2",       XrmoptionSepArg, 0     },
226 { "-time",        ".time",        XrmoptionSepArg, 0     },
227 { "-gamma",       ".gamma",       XrmoptionSepArg, 0     },
228 { "-font",        ".font",        XrmoptionSepArg, 0     },
229 { "-mono",        ".mono",        XrmoptionNoArg,  "on"  },
230 { "-nomono",      ".mono",        XrmoptionNoArg,  "off" },
231 { "-root",        ".root",        XrmoptionNoArg,  "on"  },
232 { "-noroot",      ".root",        XrmoptionNoArg,  "off" },
233 { "-geometry",    ".geometry",    XrmoptionSepArg, 0     },
234 { "-title",       ".title",       XrmoptionSepArg, 0     },
235 { "-iconname",    ".iconname",    XrmoptionSepArg, 0     },
236 };
237 
238 
command_line_x(argc,argv)239 void command_line_x(argc, argv)
240      int   argc;
241      char *argv[];
242 {
243   init_x_general(argc, argv);
244   process_opts();
245 
246   init_x_colors();
247   init_x_pixmaps();
248   font = load_x_font(dsply, font_name);
249 
250   if (use_root)
251     init_x_root_window();
252   else
253     init_x_separate_window();
254 }
255 
256 
init_x_general(argc,argv)257 static void init_x_general(argc, argv)
258      int   argc;
259      char *argv[];
260 {
261   progname  = argv[0];
262   progclass = "XEarth";
263   app_shell = XtAppInitialize(&app_context, progclass,
264                               options, XtNumber(options),
265                               &argc, argv, defaults, 0, 0);
266   if (argc > 1) usage(NULL);
267 
268   dsply = XtDisplay(app_shell);
269   scrn  = DefaultScreen(dsply);
270   db    = XtDatabase(dsply);
271 
272   XtGetApplicationNameAndClass(dsply, &progname, &progclass);
273 
274   root   = RootWindow(dsply, scrn);
275   cmap   = DefaultColormap(dsply, scrn);
276   visl   = DefaultVisual(dsply, scrn);
277   dpth   = DefaultDepth(dsply, scrn);
278   wdth   = DisplayWidth(dsply, scrn);
279   hght   = DisplayHeight(dsply, scrn);
280   white  = WhitePixel(dsply, scrn);
281   black  = BlackPixel(dsply, scrn);
282   gc     = XCreateGC(dsply, root, 0, NULL);
283   hlight = white;
284   XSetState(dsply, gc, white, black, GXcopy, AllPlanes);
285 
286   bpp = get_bits_per_pixel(dpth);
287 }
288 
289 
process_opts()290 static void process_opts()
291 {
292   if (get_boolean_resource("version", "Version"))
293     version_info(1);
294 
295   if (get_boolean_resource("showmarkers", "Showmarkers"))
296   {
297     markerfile = get_string_resource("markerfile", "Markerfile");
298     show_marker_info(markerfile);
299   }
300 
301   /* process complex resources
302    */
303   get_proj_type();
304   get_viewing_position();
305   get_sun_position();
306   get_size();
307   get_shift();
308   get_labelpos();
309   get_rotation();
310   get_geometry();
311 
312   /* process simple resources
313    */
314   view_mag        = get_float_resource("mag", "Mag");
315   do_shade        = get_boolean_resource("shade", "Shade");
316   do_label        = get_boolean_resource("label", "Label");
317   do_markers      = get_boolean_resource("markers", "Markers");
318   markerfile      = get_string_resource("markerfile", "Markerfile");
319   wait_time       = get_integer_resource("wait", "Wait");
320   time_warp       = get_float_resource("timewarp", "Timewarp");
321   day             = get_integer_resource("day", "Day");
322   night           = get_integer_resource("night", "Night");
323   terminator      = get_integer_resource("term", "Term");
324   use_two_pixmaps = get_boolean_resource("twopix", "Twopix");
325   num_colors      = get_integer_resource("ncolors", "Ncolors");
326   do_fork         = get_boolean_resource("fork", "Fork");
327   do_once         = get_boolean_resource("once", "Once");
328   priority        = get_integer_resource("nice", "Nice");
329   do_stars        = get_boolean_resource("stars", "Stars");
330   star_freq       = get_float_resource("starfreq", "Starfreq");
331   big_stars       = get_integer_resource("bigstars", "Bigstars");
332   do_grid         = get_boolean_resource("grid", "Grid");
333   grid_big        = get_integer_resource("grid1", "Grid1");
334   grid_small      = get_integer_resource("grid2", "Grid2");
335   fixed_time      = get_integer_resource("time", "Time");
336   xgamma          = get_float_resource("gamma", "Gamma");
337   font_name       = get_string_resource("font", "Font");
338   mono            = get_boolean_resource("mono", "Mono");
339 
340   /* various sanity checks on simple resources
341    */
342   if ((view_rot < -180) || (view_rot > 360))
343     fatal("viewing rotation must be between -180 and 360");
344   if (view_mag <= 0)
345     fatal("viewing magnification must be positive");
346   if (wait_time < 0)
347     fatal("arg to -wait must be non-negative");
348   if (time_warp <= 0)
349     fatal("arg to -timewarp must be positive");
350   if ((num_colors < 3) || (num_colors > 1024))
351     fatal("arg to -ncolors must be between 3 and 1024");
352   if ((star_freq < 0) || (star_freq > 1))
353     fatal("arg to -starfreq must be between 0 and 1");
354   if ((big_stars < 0) || (big_stars > 100))
355     fatal("arg to -bigstars must be between 0 and 100");
356   if (grid_big <= 0)
357     fatal("arg to -grid1 must be positive");
358   if (grid_small <= 0)
359     fatal("arg to -grid2 must be positive");
360   if ((day > 100) || (day < 0))
361     fatal("arg to -day must be between 0 and 100");
362   if ((night > 100) || (night < 0))
363     fatal("arg to -night must be between 0 and 100");
364   if ((terminator > 100) || (terminator < 0))
365     fatal("arg to -term must be between 0 and 100");
366   if (xgamma <= 0)
367     fatal("arg to -gamma must be positive");
368 
369   /* if we're only rendering once, make sure we don't
370    * waste memory by allocating two pixmaps
371    */
372   if (do_once)
373     use_two_pixmaps = 0;
374 
375   /* if we're working with a one-bit display, force -mono mode
376    */
377   if (dpth == 1)
378     mono = 1;
379 }
380 
381 
init_x_colors()382 static void init_x_colors()
383 {
384   int     i;
385   XColor  xc, junk;
386   u_char *tmp;
387   double  inv_xgamma;
388 
389   if (mono)
390   {
391     mono_dither_setup();
392     pels = (Pixel *) malloc((unsigned) sizeof(Pixel) * 2);
393     assert(pels != NULL);
394     pels[0] = black;
395     pels[1] = white;
396   }
397   else
398   {
399     if (XAllocNamedColor(dsply, cmap, "red", &xc, &junk) != 0)
400       hlight = xc.pixel;
401 
402     dither_setup(num_colors);
403     pels = (Pixel *) malloc((unsigned) sizeof(Pixel) * dither_ncolors);
404     assert(pels != NULL);
405 
406     tmp = dither_colormap;
407     inv_xgamma = 1.0 / xgamma;
408     for (i=0; i<dither_ncolors; i++)
409     {
410       xc.red   = ((1<<16)-1) * pow(((double) tmp[0] / 255), inv_xgamma);
411       xc.green = ((1<<16)-1) * pow(((double) tmp[1] / 255), inv_xgamma);
412       xc.blue  = ((1<<16)-1) * pow(((double) tmp[2] / 255), inv_xgamma);
413 
414       if (XAllocColor(dsply, cmap, &xc) == 0)
415         fatal("unable to allocate enough colors");
416       pels[i] = xc.pixel;
417 
418       tmp += 3;
419     }
420   }
421 }
422 
423 
init_x_pixmaps()424 static void init_x_pixmaps()
425 {
426   work_pix = XCreatePixmap(dsply, root, (unsigned) wdth,
427                            (unsigned) hght, (unsigned) dpth);
428   if (use_two_pixmaps)
429     disp_pix = XCreatePixmap(dsply, root, (unsigned) wdth,
430                              (unsigned) hght, (unsigned) dpth);
431 }
432 
433 
init_x_root_window()434 static void init_x_root_window()
435 {
436   xearth_window = GetVRoot(dsply);
437 
438   /* try to free any resources retained by any previous clients that
439    * scribbled in the root window (also deletes the _XSETROOT_ID
440    * property from the root window, if it was there)
441    */
442   freePrevious(dsply, xearth_window);
443 
444   /* 18 may 1994
445    *
446    * setting the _XSETROOT_ID property is dangerous if xearth might be
447    * killed by other means (e.g., from the shell), because some other
448    * client might allocate a resource with the same resource ID that
449    * xearth had stored in the _XSETROOT_ID property, so subsequent
450    * attempts to free any resources retained by a client that had
451    * scribbled on the root window via XKillClient() may end up killing
452    * the wrong client.
453    *
454    * this possibility could be eliminated by setting the closedown
455    * mode for the display connection to RetainPermanent, but this
456    * seemed to be causing core dumps in an R5pl26 server -- i submitted
457    * a bug report to the X consortium about this. i _think_ the server
458    * core dumps were related to the fact that xearth can sleep for a
459    * _long_ time between protocol requests, perhaps longer than it
460    * takes for one server to die (e.g., when somebody logs out) and a
461    * new server to be restarted, and somehow exercising the display
462    * connection from the old server was causing the new one to crash?
463    *
464    * possible fixes:
465    *
466    * - replace the big sleep() with a loop that interleaves sleep(1)
467    *   and, say, calls to XNoOp() to test the display connection;
468    *   presumably one second is short enough to avoid the possibility
469    *   of one server dying and another restarting before a call to
470    *   XNoOp() catches the fact that the connection to the old server
471    *   died.
472    *
473    * - use RetainTemporary mode instead of RetainPermanent? need to
474    *   check the X documentation and figure out exactly what this
475    *   would mean.
476    *
477    * it would be nice to install the _XSETROOT_ID property so xearth
478    * interoperates gracefully with other things that try to scribble
479    * on the root window (e.g., xsetroot, xloadimage, xv), but until i
480    * figure out a fix to the problems described above, probably best
481    * not to bother.
482    */
483   /* preserveResource(dsply, xearth_window); */
484 }
485 
486 
init_x_separate_window()487 static void init_x_separate_window()
488 {
489   XSizeHints *xsh;
490   char       *title;
491   char       *iname;
492 
493   xearth_window = XCreateSimpleWindow(dsply,
494                                       root,
495                                       window_xvalue,
496                                       window_yvalue,
497                                       wdth,
498                                       hght,
499                                       DefaultBorderWidth,
500                                       white,
501                                       black);
502 
503   xsh = XAllocSizeHints();
504   xsh->width       = wdth;
505   xsh->height      = hght;
506   xsh->min_width   = wdth;
507   xsh->min_height  = hght;
508   xsh->max_width   = wdth;
509   xsh->max_height  = hght;
510   xsh->base_width  = wdth;
511   xsh->base_height = hght;
512   xsh->flags       = (PSize|PMinSize|PMaxSize|PBaseSize);
513 
514   if (window_pos_flag)
515   {
516     xsh->x           = window_xvalue;
517     xsh->y           = window_yvalue;
518     xsh->win_gravity = window_gravity;
519     xsh->flags |= (USPosition|PWinGravity);
520   }
521 
522   title = get_string_resource("title", "Title");
523   iname = get_string_resource("iconname", "Iconname");
524   if ((title == NULL) || (iname == NULL))
525     fatal("title or iconname is NULL (this shouldn't happen)");
526 
527   XSetWMNormalHints(dsply, xearth_window, xsh);
528   XStoreName(dsply, xearth_window, title);
529   XSetIconName(dsply, xearth_window, iname);
530 
531   XMapRaised(dsply, xearth_window);
532   XSync(dsply, False);
533 
534   XFree((char *) xsh);
535   free(title);
536   free(iname);
537 }
538 
539 
x11_output()540 void x11_output()
541 {
542   while (1)
543   {
544     compute_positions();
545 
546     /* if we were really clever, we'd only
547      * do this if the position has changed
548      */
549     scan_map();
550     do_dots();
551 
552     /* for now, go ahead and reload the marker info every time
553      * we redraw, but maybe change this in the future?
554      */
555     load_marker_info(markerfile);
556 
557     x11_setup();
558     render(x11_row);
559     x11_cleanup();
560 
561     if (do_once)
562     {
563       if (use_root)
564         preserveResource(dsply, xearth_window);
565       XSync(dsply, True);
566       return;
567     }
568 
569     /* schedule an alarm for wait_time seconds and pause. alarm() and
570      * pause() are used instead of sleep so that if xearth is sent a
571      * SIGSTOP and SIGCONT separated by more than wait_time, it will
572      * refresh the screen as soon as the SIGCONT is received. this
573      * facilitates graceful interaction with things like FvwmBacker.
574      * (thanks to Richard Everson for passing this along.)
575      */
576     signal(SIGALRM, wakeup);
577     signal(SIGCONT, wakeup);
578     if (wait_time > 0)
579     {
580       /* only do the alarm()/pause() stuff if wait_time is non-zero,
581        * else alarm() will not behave as desired.
582        */
583       alarm(wait_time);
584       pause();
585     }
586   }
587 }
588 
589 
590 /* no-op signal handler for catching SIGALRM and SIGCONT
591  * (used to wake up from pause() system call)
592  */
wakeup(int junk)593 static void wakeup(int junk)
594 {
595   /* nothing */
596 }
597 
598 
599 /* determine bits_per_pixel value for pixmaps of specified depth
600  */
get_bits_per_pixel(depth)601 static int get_bits_per_pixel(depth)
602      int depth;
603 {
604   int                  i;
605   int                  cnt;
606   XPixmapFormatValues *pmf;
607   int                  rslt;
608 
609   pmf = XListPixmapFormats(dsply, &cnt);
610   if (pmf == NULL)
611     fatal("unable to get pixmap format list");
612 
613   rslt = 0;
614   for (i=0; i<cnt; i++)
615     if (pmf[i].depth == depth)
616     {
617       rslt = pmf[i].bits_per_pixel;
618       break;
619     }
620 
621   if (rslt == 0)
622     fatal("unable to determine pixmap format");
623 
624   XFree(pmf);
625 
626   return rslt;
627 }
628 
629 
load_x_font(dpy,fontname)630 static XFontStruct *load_x_font(dpy, fontname)
631      Display *dpy;
632      char    *fontname;
633 {
634   XFontStruct *rslt;
635 
636   rslt = XLoadQueryFont(dpy, fontname);
637   if (rslt == NULL)
638   {
639     rslt = XQueryFont(dpy, XGContextFromGC(gc));
640     if (rslt == NULL)
641       fatal("completely unable to load fonts");
642     else
643       warning("unable to load font, reverting to default");
644   }
645   else
646   {
647     XSetFont(dpy, gc, rslt->fid);
648   }
649 
650   return rslt;
651 }
652 
653 
654 /* fetch and decode 'proj' resource (projection type)
655  */
get_proj_type()656 static void get_proj_type()
657 {
658   char *res;
659 
660   res = get_string_resource("proj", "Proj");
661   if (res != NULL)
662   {
663     decode_proj_type(res);
664     free(res);
665   }
666 }
667 
668 
669 /* fetch and decode 'pos' resource (viewing position specifier)
670  */
get_viewing_position()671 static void get_viewing_position()
672 {
673   char *res;
674 
675   res = get_string_resource("pos", "Pos");
676   if (res != NULL)
677   {
678     decode_viewing_pos(res);
679     free(res);
680   }
681 }
682 
683 
684 /* fetch and decode 'sunpos' resource (sun position specifier)
685  */
get_sun_position()686 static void get_sun_position()
687 {
688   char *res;
689 
690   res = get_string_resource("sunpos", "Sunpos");
691   if (res != NULL)
692   {
693     decode_sun_pos(res);
694     free(res);
695   }
696 }
697 
698 
699 /* fetch and decode 'rot' resource (rotation specifier)
700  */
get_rotation()701 static void get_rotation()
702 {
703   char *res;
704 
705   res = get_string_resource("rot", "Rotation");
706   if (res != NULL)
707   {
708     decode_rotation(res);
709     free(res);
710   }
711 }
712 
713 
714 /* fetch and decode 'size' resource (size specifier)
715  */
get_size()716 static void get_size()
717 {
718   char *res;
719 
720   res = get_string_resource("size", "Size");
721   if (res != NULL)
722   {
723     decode_size(res);
724     free(res);
725   }
726 }
727 
728 
729 /* fetch and decode 'shift' resource (shift specifier)
730  */
get_shift()731 static void get_shift()
732 {
733   char *res;
734 
735   res = get_string_resource("shift", "Shift");
736   if (res != NULL)
737   {
738     decode_shift(res);
739     free(res);
740   }
741 }
742 
743 
744 /* fetch and decode 'labelpos' resource (label position)
745  */
get_labelpos()746 static void get_labelpos()
747 {
748   char    *res;
749   int      mask;
750   int      x, y;
751   unsigned w, h;
752 
753   /* it's somewhat brute-force ugly to hard-code these here,
754    * duplicating information contained in defaults[], but such it is.
755    */
756   label_orient = 0;
757   label_xvalue = wdth - 5;
758   label_yvalue = hght - 5;
759 
760   res = get_string_resource("labelpos", "Labelpos");
761   if (res != NULL)
762   {
763     mask = XParseGeometry(res, &x, &y, &w, &h);
764 
765     if (mask & (WidthValue | HeightValue))
766       warning("width and height ignored in label position");
767 
768     if ((mask & XValue) && (mask & YValue))
769     {
770       label_xvalue = x;
771       label_yvalue = y;
772 
773       if ((mask & XNegative) == 0)
774         label_orient |= LABEL_LEFT_FLUSH;
775 
776       if ((mask & YNegative) == 0)
777         label_orient |= LABEL_TOP_FLUSH;
778     }
779     else
780     {
781       warning("label position must specify x and y offsets");
782     }
783 
784     free(res);
785   }
786 }
787 
788 
789 /* fetch and decode 'root' and 'geometry' resource (whether to render
790  * into root or separate window; if separate window, position of that
791  * window) [this is pretty ugly code, but it gets the job done ...]
792  */
get_geometry()793 static void get_geometry()
794 {
795   int   check_geom;
796   char *res;
797   int   mask;
798   int   x, y;
799   unsigned int   w, h;
800 
801   res = get_string_resource("root", "Root");
802   if (res != NULL)
803   {
804     free(res);
805     if (get_boolean_resource("root", "Root"))
806     {
807       /* user specified -root; render into the root window
808        * (ignore any -geometry, if provided)
809        */
810       use_root   = 1;
811       check_geom = 0;
812     }
813     else
814     {
815       /* user specified -noroot; render into separate window
816        */
817       use_root   = 0;
818       check_geom = 1;
819     }
820   }
821   else
822   {
823     /* user specified neither -root nor -noroot; if -geometry is
824      * provided, render into separate window, else render into root
825      */
826     use_root   = 1;
827     check_geom = 1;
828   }
829 
830   /* if check_geom isn't set, nothing more to do
831    */
832   if (check_geom == 0) return;
833 
834   /* look for -geometry and try to make sense of it
835    */
836   res = get_string_resource("geometry", "Geometry");
837   if (res != NULL)
838   {
839     /* if -geometry is specified, assume -noroot and set default width
840      * and height (which get overridden by -geometry width and height,
841      * if provided)
842      */
843     use_root = 0;
844     wdth     = DefaultWdthHght;
845     hght     = DefaultWdthHght;
846 
847     mask = XParseGeometry(res, &x, &y, &w, &h);
848 
849     /* extract width and height information
850      */
851     if ((mask & WidthValue) && (mask & HeightValue))
852     {
853       wdth = w;
854       hght = h;
855     }
856     else
857     {
858       if ((mask & WidthValue) || (mask & HeightValue))
859         warning("geometry must specify both width and height");
860     }
861 
862     /* extract position information
863      */
864     if ((mask & XValue) && (mask & YValue))
865     {
866       window_pos_flag = 1;
867       window_xvalue   = x;
868       window_yvalue   = y;
869 
870       if (mask & XNegative)
871       {
872         window_xvalue += (DisplayWidth(dsply, scrn) - wdth);
873         window_xvalue -= (2 * DefaultBorderWidth);
874       }
875 
876       if (mask & YNegative)
877       {
878         window_yvalue += (DisplayHeight(dsply, scrn) - hght);
879         window_yvalue -= (2 * DefaultBorderWidth);
880       }
881 
882       if (mask & XNegative)
883         if (mask & YNegative)
884           window_gravity = SouthEastGravity;
885         else
886           window_gravity = NorthEastGravity;
887       else
888         if (mask & YNegative)
889           window_gravity = SouthWestGravity;
890         else
891           window_gravity = NorthWestGravity;
892     }
893     else
894     {
895       if ((mask & XValue) || (mask & YValue))
896         warning("geometry must specify both x and y offsets");
897 
898       window_pos_flag = 0;
899       window_xvalue   = 0;
900       window_yvalue   = 0;
901       window_gravity  = 0;
902     }
903 
904     free(res);
905   }
906   else if (use_root == 0)
907   {
908     /* if -noroot was specified but no -geometry was provided, assume
909      * defaults
910      */
911     wdth            = DefaultWdthHght;
912     hght            = DefaultWdthHght;
913     window_pos_flag = 0;
914     window_xvalue   = 0;
915     window_yvalue   = 0;
916   }
917 }
918 
919 
x11_setup()920 static void x11_setup()
921 {
922   unsigned dith_size;
923   unsigned xbuf_size;
924 
925   switch (bpp)
926   {
927   case 1:
928     dith_size = wdth + 7;
929     break;
930 
931   case 8:
932   case 16:
933   case 24:
934   case 32:
935     dith_size = wdth;
936     break;
937 
938   default:
939     dith_size = 0; /* keep lint happy */
940     fprintf(stderr,
941             "xearth %s: fatal - unsupported pixmap format (%d bits/pixel)\n",
942             VersionString, bpp);
943     exit(1);
944   }
945 
946   xbuf_size = (dith_size * bpp) >> 3;
947 
948   dith = (u16or32 *) malloc((unsigned) sizeof(u16or32) * dith_size);
949   assert(dith != NULL);
950 
951   xbuf = (u_char *) malloc((unsigned) xbuf_size);
952   assert(xbuf != NULL);
953 
954   xim = XCreateImage(dsply, visl, (unsigned) dpth, ZPixmap, 0,
955                      (char *) xbuf, (unsigned) wdth, 1, 8,
956                      xbuf_size);
957 
958   if (xim->bits_per_pixel != bpp)
959   {
960     fprintf(stderr,
961             "xearth %s: fatal - unexpected bits/pixel for depth %d\n",
962             VersionString, dpth);
963     fprintf(stderr,
964             "  (expected %d bits/pixel, actual value is %d)\n",
965             bpp, xim->bits_per_pixel);
966     exit(1);
967   }
968 
969   if (bpp == 1)
970   {
971     /* force MSBFirst bitmap_bit_order and byte_order
972      */
973     xim->bitmap_bit_order = MSBFirst;
974     xim->byte_order       = MSBFirst;
975   }
976 
977   idx = 0;
978 }
979 
980 
981 /* pack pixels into ximage format (assuming bits_per_pixel == 1,
982  * bitmap_bit_order == MSBFirst, and byte_order == MSBFirst)
983  */
pack_mono_1(src,dst)984 static void pack_mono_1(src, dst)
985      u16or32 *src;
986      u_char  *dst;
987 {
988   int      i, i_lim;
989   unsigned val;
990 
991   i_lim = wdth;
992   for (i=0; i<i_lim; i+=8)
993   {
994     val = ((src[0] << 7) | (src[1] << 6) | (src[2] << 5) |
995            (src[3] << 4) | (src[4] << 3) | (src[5] << 2) |
996            (src[6] << 1) | (src[7] << 0));
997 
998     /* if white is pixel 0, need to toggle the bits
999      */
1000     dst[i>>3] = (white == 0) ? (~ val) : val;
1001     src += 8;
1002   }
1003 }
1004 
1005 
1006 /* pack pixels into ximage format (assuming bits_per_pixel == 8)
1007  */
pack_8(src,map,dst)1008 static void pack_8(src, map, dst)
1009      u16or32 *src;
1010      Pixel   *map;
1011      u_char  *dst;
1012 {
1013   int      i, i_lim;
1014   unsigned val;
1015 
1016   i_lim = wdth;
1017   for (i=0; i<i_lim; i++)
1018   {
1019     val = map[src[i]];
1020     dst[i] = val;
1021   }
1022 }
1023 
1024 
1025 /* pack pixels into ximage format (assuming bits_per_pixel == 16)
1026  */
pack_16(src,map,dst)1027 static void pack_16(src, map, dst)
1028      u16or32 *src;
1029      Pixel   *map;
1030      u_char  *dst;
1031 {
1032   int      i, i_lim;
1033   unsigned val;
1034 
1035   i_lim = wdth;
1036 
1037   if (xim->byte_order == MSBFirst)
1038   {
1039     for (i=0; i<i_lim; i++)
1040     {
1041       val    = map[src[i]];
1042       dst[0] = (val >> 8) & 0xff;
1043       dst[1] = val & 0xff;
1044       dst   += 2;
1045     }
1046   }
1047   else /* (xim->byte_order == LSBFirst) */
1048   {
1049     for (i=0; i<i_lim; i++)
1050     {
1051       val    = map[src[i]];
1052       dst[0] = val & 0xff;
1053       dst[1] = (val >> 8) & 0xff;
1054       dst   += 2;
1055     }
1056   }
1057 }
1058 
1059 
1060 /* pack pixels into ximage format (assuming bits_per_pixel == 24)
1061  */
pack_24(src,map,dst)1062 static void pack_24(src, map, dst)
1063      u16or32 *src;
1064      Pixel   *map;
1065      u_char  *dst;
1066 {
1067   int      i, i_lim;
1068   unsigned val;
1069 
1070   i_lim = wdth;
1071 
1072   if (xim->byte_order == MSBFirst)
1073   {
1074     for (i=0; i<i_lim; i++)
1075     {
1076       val    = map[src[i]];
1077       dst[0] = (val >> 16) & 0xff;
1078       dst[1] = (val >> 8) & 0xff;
1079       dst[2] = val & 0xff;
1080       dst   += 3;
1081     }
1082   }
1083   else /* (xim->byte_order == LSBFirst) */
1084   {
1085     for (i=0; i<i_lim; i++)
1086     {
1087       val    = map[src[i]];
1088       dst[0] = val & 0xff;
1089       dst[1] = (val >> 8) & 0xff;
1090       dst[2] = (val >> 16) & 0xff;
1091       dst   += 3;
1092     }
1093   }
1094 }
1095 
1096 
1097 /* pack pixels into ximage format (assuming bits_per_pixel == 32)
1098  */
pack_32(src,map,dst)1099 static void pack_32(src, map, dst)
1100      u16or32 *src;
1101      Pixel   *map;
1102      u_char  *dst;
1103 {
1104   int      i, i_lim;
1105   unsigned val;
1106 
1107   i_lim = wdth;
1108 
1109   if (xim->byte_order == MSBFirst)
1110   {
1111     for (i=0; i<i_lim; i++)
1112     {
1113       val    = map[src[i]];
1114       dst[0] = (val >> 24) & 0xff;
1115       dst[1] = (val >> 16) & 0xff;
1116       dst[2] = (val >> 8) & 0xff;
1117       dst[3] = val & 0xff;
1118       dst   += 4;
1119     }
1120   }
1121   else /* (xim->byte_order == LSBFirst) */
1122   {
1123     for (i=0; i<i_lim; i++)
1124     {
1125       val    = map[src[i]];
1126       dst[0] = val & 0xff;
1127       dst[1] = (val >> 8) & 0xff;
1128       dst[2] = (val >> 16) & 0xff;
1129       dst[3] = (val >> 24) & 0xff;
1130       dst   += 4;
1131     }
1132   }
1133 }
1134 
1135 
x11_row(row)1136 static int x11_row(row)
1137      u_char *row;
1138 {
1139   if (mono)
1140     mono_dither_row(row, dith);
1141   else
1142     dither_row(row, dith);
1143 
1144   switch (bpp)
1145   {
1146   case 1:
1147     pack_mono_1(dith, xbuf);
1148     break;
1149 
1150   case 8:
1151     pack_8(dith, pels, xbuf);
1152     break;
1153 
1154   case 16:
1155     pack_16(dith, pels, xbuf);
1156     break;
1157 
1158   case 24:
1159     pack_24(dith, pels, xbuf);
1160     break;
1161 
1162   case 32:
1163     pack_32(dith, pels, xbuf);
1164     break;
1165 
1166   default:
1167     fprintf(stderr,
1168             "xearth %s: fatal - unsupported pixmap format (%d bits/pixel)\n",
1169             VersionString, bpp);
1170     exit(1);
1171   }
1172 
1173   XPutImage(dsply, work_pix, gc, xim, 0, 0, 0, idx, (unsigned) wdth, 1);
1174   idx += 1;
1175 
1176   return 0;
1177 }
1178 
1179 
x11_cleanup()1180 static void x11_cleanup()
1181 {
1182   MarkerInfo *minfo;
1183   Display    *dpy;
1184   Pixmap      tmp;
1185 
1186   XDestroyImage(xim);
1187   free(dith);
1188 
1189   dpy = dsply;
1190 
1191   if (do_markers)
1192   {
1193     minfo = marker_info;
1194     while (minfo->label != NULL)
1195     {
1196       mark_location(dpy, minfo);
1197       minfo += 1;
1198     }
1199   }
1200 
1201   if (do_label) draw_label(dpy);
1202 
1203   XSetWindowBackgroundPixmap(dpy, xearth_window, work_pix);
1204   XClearWindow(dpy, xearth_window);
1205   XSync(dpy, True);
1206 
1207   if (use_two_pixmaps)
1208   {
1209     tmp      = work_pix;
1210     work_pix = disp_pix;
1211     disp_pix = tmp;
1212   }
1213 }
1214 
1215 
draw_label(dpy)1216 static void draw_label(dpy)
1217      Display *dpy;
1218 {
1219   int         dy;
1220   int         x, y;
1221   int         len;
1222   int         direction;
1223   int         ascent;
1224   int         descent;
1225   char        buf[128];
1226   XCharStruct extents;
1227 
1228   dy = font->ascent + font->descent + 1;
1229 
1230   if (label_orient & LABEL_TOP_FLUSH)
1231   {
1232     y = label_yvalue + font->ascent;
1233   }
1234   else
1235   {
1236     y = (hght + label_yvalue) - font->descent;
1237 #ifdef DEBUG
1238     y -= 3 * dy;                /* 4 lines of text */
1239 #else
1240     y -= 2 * dy;                /* 3 lines of text */
1241 #endif
1242   }
1243 
1244 #ifdef DEBUG
1245   frame += 1;
1246   sprintf(buf, "frame %d", frame);
1247   len = strlen(buf);
1248   XTextExtents(font, buf, len, &direction, &ascent, &descent, &extents);
1249   if (label_orient & LABEL_LEFT_FLUSH)
1250     x = label_xvalue - extents.lbearing;
1251   else
1252     x = (wdth + label_xvalue) - extents.rbearing;
1253   draw_outlined_string(dpy, work_pix, white, black, x, y, buf, len);
1254   y += dy;
1255 #endif /* DEBUG */
1256 
1257   strftime(buf, sizeof(buf), "%d %b %y %H:%M %Z", localtime(&current_time));
1258   len = strlen(buf);
1259   XTextExtents(font, buf, len, &direction, &ascent, &descent, &extents);
1260   if (label_orient & LABEL_LEFT_FLUSH)
1261     x = label_xvalue - extents.lbearing;
1262   else
1263     x = (wdth + label_xvalue) - extents.rbearing;
1264   draw_outlined_string(dpy, work_pix, white, black, x, y, buf, len);
1265   y += dy;
1266 
1267   sprintf(buf, "view %.1f %c %.1f %c",
1268           fabs(view_lat), ((view_lat < 0) ? 'S' : 'N'),
1269           fabs(view_lon), ((view_lon < 0) ? 'W' : 'E'));
1270   len = strlen(buf);
1271   XTextExtents(font, buf, len, &direction, &ascent, &descent, &extents);
1272   if (label_orient & LABEL_LEFT_FLUSH)
1273     x = label_xvalue - extents.lbearing;
1274   else
1275     x = (wdth + label_xvalue) - extents.rbearing;
1276   draw_outlined_string(dpy, work_pix, white, black, x, y, buf, len);
1277   y += dy;
1278 
1279   sprintf(buf, "sun %.1f %c %.1f %c",
1280           fabs(sun_lat), ((sun_lat < 0) ? 'S' : 'N'),
1281           fabs(sun_lon), ((sun_lon < 0) ? 'W' : 'E'));
1282   len = strlen(buf);
1283   XTextExtents(font, buf, len, &direction, &ascent, &descent, &extents);
1284   if (label_orient & LABEL_LEFT_FLUSH)
1285     x = label_xvalue - extents.lbearing;
1286   else
1287     x = (wdth + label_xvalue) - extents.rbearing;
1288   draw_outlined_string(dpy, work_pix, white, black, x, y, buf, len);
1289   y += dy;
1290 }
1291 
1292 
mark_location(dpy,info)1293 static void mark_location(dpy, info)
1294      Display    *dpy;
1295      MarkerInfo *info;
1296 {
1297   int         x, y;
1298   int         len;
1299   double      lat, lon;
1300   double      pos[3];
1301   char       *text;
1302   int         direction;
1303   int         ascent;
1304   int         descent;
1305   XCharStruct extents;
1306 
1307   lat = info->lat * (M_PI/180);
1308   lon = info->lon * (M_PI/180);
1309 
1310   pos[0] = sin(lon) * cos(lat);
1311   pos[1] = sin(lat);
1312   pos[2] = cos(lon) * cos(lat);
1313 
1314   XFORM_ROTATE(pos, view_pos_info);
1315 
1316   if (proj_type == ProjTypeOrthographic)
1317   {
1318     /* if the marker isn't visible, return immediately
1319      */
1320     if (pos[2] <= 0) return;
1321   }
1322   else if (proj_type == ProjTypeMercator)
1323   {
1324     /* apply mercator projection
1325      */
1326     pos[0] = MERCATOR_X(pos[0], pos[2]);
1327     pos[1] = MERCATOR_Y(pos[1]);
1328   }
1329   else /* (proj_type == ProjTypeCylindrical) */
1330   {
1331     /* apply cylindrical projection
1332      */
1333     pos[0] = CYLINDRICAL_X(pos[0], pos[2]);
1334     pos[1] = CYLINDRICAL_Y(pos[1]);
1335   }
1336 
1337   x = XPROJECT(pos[0]);
1338   y = YPROJECT(pos[1]);
1339 
1340   XSetForeground(dpy, gc, black);
1341   XDrawArc(dpy, work_pix, gc, x-3, y-3, 6, 6, 0, 360*64);
1342   XDrawArc(dpy, work_pix, gc, x-1, y-1, 2, 2, 0, 360*64);
1343   XSetForeground(dpy, gc, hlight);
1344   XDrawArc(dpy, work_pix, gc, x-2, y-2, 4, 4, 0, 360*64);
1345 
1346   text = info->label;
1347   if (text != NULL)
1348   {
1349     len = strlen(text);
1350     XTextExtents(font, text, len, &direction, &ascent, &descent, &extents);
1351 
1352     switch (info->align)
1353     {
1354     case MarkerAlignLeft:
1355       x -= (extents.rbearing + 4);
1356       y += (font->ascent + font->descent) / 3;
1357       break;
1358 
1359     case MarkerAlignRight:
1360     case MarkerAlignDefault:
1361       x += (extents.lbearing + 3);
1362       y += (font->ascent + font->descent) / 3;
1363       break;
1364 
1365     case MarkerAlignAbove:
1366       x -= (extents.rbearing - extents.lbearing) / 2;
1367       y -= (extents.descent + 4);
1368       break;
1369 
1370     case MarkerAlignBelow:
1371       x -= (extents.rbearing - extents.lbearing) / 2;
1372       y += (extents.ascent + 5);
1373       break;
1374 
1375     default:
1376       assert(0);
1377     }
1378 
1379     draw_outlined_string(dpy, work_pix, hlight, black, x, y, text, len);
1380   }
1381 
1382   XSetForeground(dpy, gc, white);
1383 }
1384 
1385 
draw_outlined_string(dpy,pix,fg,bg,x,y,text,len)1386 static void draw_outlined_string(dpy, pix, fg, bg, x, y, text, len)
1387      Display *dpy;
1388      Pixmap   pix;
1389      Pixel    fg;
1390      Pixel    bg;
1391      int      x;
1392      int      y;
1393      char    *text;
1394      int      len;
1395 {
1396   XSetForeground(dpy, gc, bg);
1397   XDrawString(dpy, pix, gc, x+1, y, text, len);
1398   XDrawString(dpy, pix, gc, x-1, y, text, len);
1399   XDrawString(dpy, pix, gc, x, y+1, text, len);
1400   XDrawString(dpy, pix, gc, x, y-1, text, len);
1401   XSetForeground(dpy, gc, fg);
1402   XDrawString(dpy, pix, gc, x, y, text, len);
1403 }
1404 
1405 
1406 /* Function Name: GetVRoot
1407  * Description: Gets the root window, even if it's a virtual root
1408  * Arguments: the display and the screen
1409  * Returns: the root window for the client
1410  *
1411  * (taken nearly verbatim from the june 1993 comp.windows.x FAQ, item 148)
1412  */
GetVRoot(dpy)1413 static Window GetVRoot(dpy)
1414      Display *dpy;
1415 {
1416   int          i;
1417   Window       rootReturn, parentReturn, *children;
1418   unsigned int numChildren;
1419   Atom         __SWM_VROOT = None;
1420   Window       rslt = root;
1421 
1422   __SWM_VROOT = XInternAtom(dpy, "__SWM_VROOT", False);
1423   XQueryTree(dpy, root, &rootReturn, &parentReturn, &children, &numChildren);
1424   for (i=0; i<numChildren; i++)
1425   {
1426     Atom          actual_type;
1427     int           actual_format;
1428     unsigned long nitems, bytesafter;
1429     Window       *newRoot = NULL;
1430 
1431     /* item 148 in the FAQ neglects to mention that there is a race
1432      * condition here; consider a child of the root window that
1433      * existed when XQueryTree() was called, but has disappeared
1434      * before XGetWindowProperty() gets called for that window ...
1435      */
1436     if ((XGetWindowProperty(dpy, children[i], __SWM_VROOT, 0, 1,
1437                             False, XA_WINDOW, &actual_type,
1438                             &actual_format, &nitems, &bytesafter,
1439                             (unsigned char **) &newRoot) == Success)
1440         && newRoot)
1441     {
1442       rslt = *newRoot;
1443       break;
1444     }
1445   }
1446 
1447   /* item 148 in the FAQ also neglects to mention that we probably
1448    * want to free the list of children after we're done with it ...
1449    */
1450   XFree((void *) children);
1451 
1452   return rslt;
1453 }
1454 
1455 
1456 /*
1457  * the following code is lifted nearly verbatim from jim frost's
1458  * xloadimage code (version 3.00). that code includes a note
1459  * indicating that the changes to allow proper freeing of previously
1460  * allocated resources made by Deron Dann Johnson (dj@eng.sun.com),
1461  * thus he may well be the author of this code.
1462  *
1463  * Copyright (C) 1989, 1990, 1991 by Jim Frost.
1464  *
1465  * xkill_handler() and the XSetErrorHandler() code in freePrevious()
1466  * were not in the original xloadimage code; this is new as of xearth
1467  * version 0.91.
1468  */
1469 
updateProperty(dpy,w,name,type,format,data,nelem)1470 static void updateProperty(dpy, w, name, type, format, data, nelem)
1471      Display    *dpy;
1472      Window      w;
1473      const char *name;
1474      Atom        type;
1475      int         format;
1476      int         data;
1477      int         nelem;
1478 {
1479   /* intern the property name */
1480   Atom atom = XInternAtom(dpy, name, 0);
1481 
1482   /* create or replace the property */
1483   XChangeProperty(dpy, w, atom, type, format, PropModeReplace,
1484                   (unsigned char *)&data, nelem);
1485 }
1486 
1487 
1488 /* Sets the close-down mode of the client to 'RetainPermanent'
1489  * so all client resources will be preserved after the client
1490  * exits.  Puts a property on the default root window containing
1491  * an XID of the client so that the resources can later be killed.
1492  */
preserveResource(dpy,w)1493 static void preserveResource(dpy, w)
1494      Display *dpy;
1495      Window   w;
1496 {
1497   /* create dummy resource */
1498   Pixmap pm = XCreatePixmap(dpy, w, 1, 1, 1);
1499 
1500   /* create/replace the property */
1501   updateProperty(dpy, w, RETAIN_PROP_NAME, XA_PIXMAP, 32, (int)pm, 1);
1502 
1503   /* retain all client resources until explicitly killed */
1504   XSetCloseDownMode(dpy, RetainPermanent);
1505 }
1506 
1507 
1508 /* Flushes any resources previously retained by the client,
1509  * if any exist.
1510  */
freePrevious(dpy,w)1511 static void freePrevious(dpy, w)
1512      Display *dpy;
1513      Window   w;
1514 {
1515   Pixmap       *pm;
1516   Atom          actual_type;
1517   int           format;
1518   unsigned long nitems;
1519   unsigned long bytes_after;
1520 
1521   /* intern the property name */
1522   Atom atom = XInternAtom(dpy, RETAIN_PROP_NAME, 0);
1523 
1524   /* look for existing resource allocation */
1525   if ((XGetWindowProperty(dpy, w, atom, 0, 1, 1 /*delete*/,
1526                           AnyPropertyType, &actual_type,
1527                           &format, &nitems, &bytes_after,
1528                           (unsigned char **) &pm) == Success) &&
1529       (nitems == 1))
1530   {
1531     if ((actual_type == XA_PIXMAP) && (format == 32) &&
1532         (nitems == 1) && (bytes_after == 0))
1533     {
1534       /* blast it away, but first provide new X error handler in case
1535        * the client that installed the RETAIN_PROP_NAME (_XSETROOT_ID)
1536        * property on the root window has already terminated
1537        */
1538       orig_error_handler = XSetErrorHandler(xkill_handler);
1539       XKillClient(dpy, (XID) *pm);
1540       XSync(dpy, False);
1541       XSetErrorHandler(orig_error_handler);
1542       XFree((void *) pm);
1543     }
1544     else if (actual_type != None)
1545     {
1546       fprintf(stderr,
1547               "%s: warning: invalid format encountered for property %s\n",
1548               RETAIN_PROP_NAME, progname);
1549     }
1550   }
1551 }
1552 
1553 
xkill_handler(dpy,xev)1554 static int xkill_handler(dpy, xev)
1555      Display     *dpy;
1556      XErrorEvent *xev;
1557 {
1558   /* ignore any BadValue errors from the call to XKillClient() in
1559    * freePrevious(); they should only happen if the client that
1560    * installed the RETAIN_PROP_NAME (_XSETROOT_ID) property on the
1561    * root window has already terminated
1562    */
1563   if ((xev->error_code == BadValue) &&
1564       (xev->request_code == X_KillClient))
1565   {
1566     fprintf(stderr, "ignoring BadValue error from XKillClient()\n");
1567     fflush(stderr);
1568     return 0;
1569   }
1570 
1571   /* pass any other errors get on to the original error handler
1572    */
1573   return orig_error_handler(dpy, xev);
1574 }
1575