1 /*
2  * Copyright (c) 1993, 1999 Alexandre Wennmacher (wennmach@geo.Uni-Koeln.DE)
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  * This product includes software developed by Alexandre Wennmacher.
16  * 4. The name of Alexandre Wennmacher may not be used to endorse or promote
17  *    products derived from this software without specific prior written
18  *    permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY ALEXANDRE WENNMACHER AND CONTRIBUTORS ``AS IS''
21  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL ALEXANDRE WENNMACHER OR CONTRIBUTORS BE
24  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30  * POSSIBILITY OF SUCH DAMAGE.
31  *
32  */
33 
34 #include <sys/types.h>
35 #include <sys/signal.h>
36 #include <sys/time.h>
37 #include <sys/mman.h>
38 #include <stdio.h>
39 #include <math.h>
40 #include <time.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <unistd.h>
44 #include <X11/Xatom.h>
45 #include <X11/Xlib.h>
46 #include <X11/Xutil.h>
47 #include "const.h"
48 #include "Sphere.h"
49 #include "SphereDim.h"
50 #include "SphereData.h"
51 #include "color.h"
52 #include "level.h"
53 
54 /* Solaris hack */
55 #ifndef MAP_FILE
56 #define MAP_FILE 0x0
57 #endif
58 
59 #define SIZE       150
60 #define MIN_SIZE    50
61 #define X_POS       +0
62 #define Y_POS       +0
63 #define BORDER       5
64 #define SLEEP      120
65 
66 #define MIN(x,y)   (((x)<(y))?(x):(y))
67 
68 char *MainTitle[] = {"Hello, world"};
69 char *IconTitle[] = {"xworld"};
70 
71 XWMHints	xwmh = {
72     (InputHint|StateHint),
73     False,
74     NormalState,
75     0,
76     0,
77     0, 0,
78     0,
79     0,
80 };
81 
82 int GetSpherePoint(Sphere *, SPHERETYPE *, double, double);
83 long int JulianDate(int, int, int);
84 double EqunofTime(double, double *, double *);
85 char *getmyabspath(char *, char *);
86 int usage(void);
87 int main(int, char **);
88 
89 int
usage()90 usage()
91 {
92     fprintf(stderr, "usage: xworld [-geometry [=]widthxheight+x+y]"
93       " [-display dpy]\n");
94     exit(1);
95 }
96 
97 
98 static char *
alloc_image(XImage * image)99 alloc_image(XImage *image)
100 {
101     char *Map;
102     int ix, iy, j;
103     Map = (char *)malloc(image->height * image->bytes_per_line);
104     if (Map == NULL) {
105 	fprintf(stderr, "xworld: not enough memory\n");
106 	exit(1);
107     }
108     image->data = Map;
109 
110     for(j = 0, ix = 0; ix < image->width; ix++)
111 	for (iy = 0; iy < image->height; iy++)
112 	    XPutPixel(image, ix, iy, j++ % NCOLORS);
113 
114     return Map;
115 }
116 
117 int
main(int argc,char ** argv)118 main(int argc, char **argv)
119 {
120     Display                  *display;
121     char                     *displayname = NULL;
122     Window                    win;
123     GC                        gc;
124     Visual                   *visual;
125     unsigned int              class;
126     unsigned int              depth;
127     int                       screen;
128     XEvent                    event;
129     XConfigureEvent          *ConfigureEvent;
130     Mask                      InputSelectMask;
131     XSizeHints                xsh;
132     XSetWindowAttributes      xswa;
133     unsigned int              xswamask;
134     XTextProperty             xtptitle;
135     XColor                    xcolor[NCOLORS];
136     Colormap                  cmap;
137     int                       format;
138     int                       bitmap_pad;
139     XImage                   *image;
140     unsigned long             foreground, background;
141     int                       mask;
142     int                       x_return, y_return;
143     unsigned int              width_return, height_return;
144 
145     unsigned int              size, old_size;
146     unsigned int              x_pos, y_pos;
147     unsigned int              border = 1;
148     int                       n;
149     int                       r1, r2;
150 
151     int                       jr, m, d, hr, min, sec;
152     double                    juldat, sunra, sundec;
153     double                    daypart, phi;
154     struct tm                *gmt;
155     time_t                    clock;
156 
157     int                       i, j, ix, iy;
158     double                    tTheta, tPhi;
159     double                    x, y, z;
160     double                    x2, y2, z2;
161     double                    xp, yp, zp;
162     double                    cost, sint;
163     double                    cosp, sinp;
164     double                    m11, m12, m13, m21, m22, m23, m32, m33;
165     SPHERETYPE                Dummy = 1;
166     SPHERETYPE                Value;
167     Sphere                    Earth;
168     char                     *Map;
169 
170     char                     *progname;
171     FILE                     *f_o;
172     register_t               *addr;
173     const size_t              len = NPOINTS*sizeof(register_t);
174     off_t                     offset;
175     char                    **argp;
176     char                     *geometry;
177     int                       opt_geometry = 0;
178     int                       opt_display = 0;
179 
180 /*
181  * Check if xworld is installed correctly.
182  * Determine the absolute path with which xworld was invoked,
183  * open and mmap the data portion, appended to the executable.
184  */
185     progname = getmyabspath(argv[0], getenv("PATH"));
186     if (progname == NULL) {
187         fprintf(stderr, "xworld: could not determine my absolute filename.\n"
188           "Abort.\n");
189         exit(1);
190     }
191     f_o   = fopen(progname, "r");
192     if (f_o == NULL) {
193         perror("xworld: could not open myself");
194         exit(1);
195     }
196     offset = lseek(fileno(f_o), (off_t)0, SEEK_END) - len;
197     addr = (register_t *)mmap(NULL, len, PROT_READ, MAP_FILE | MAP_SHARED,
198       fileno(f_o), offset);
199     if (addr == (register_t *)MAP_FAILED) {
200         perror("xworld: could not mmap");
201         exit(1);
202     }
203 
204 /* Next, parse command line */
205     argp = argv;
206     while (*++argp != NULL) {
207        if (strcmp(*argp, "-geometry") == 0) {
208            geometry = *++argp;
209            if (geometry == NULL) usage();
210            mask = XParseGeometry(geometry, &x_return, &y_return,
211                &width_return, &height_return);
212            if (mask == 0) usage();
213            if (mask & WidthValue && mask & HeightValue)
214                if (width_return != height_return) {
215                    fprintf(stderr, "xworld: error: width must equal height!\n");
216                    exit(1);
217                }
218            opt_geometry++;
219            break;
220        }
221        if (strcmp(*argp, "-display") == 0) {
222            displayname = *++argp;
223            if (displayname == NULL) usage();
224            opt_display++;
225            break;
226        }
227        usage();
228     }
229 
230 
231 /* Open display */
232     if ((display = XOpenDisplay(displayname)) == NULL) {
233         fprintf(stderr, "xworld: can't open display %s\n",
234             XDisplayName(displayname));
235         exit(1);
236     }
237 
238     screen = DefaultScreen(display);
239     depth = DefaultDepth(display, screen);
240     if (depth == 1) {
241         fprintf(stderr, "xworld: Sorry, I can't yet work with a display of"
242             " depth 1\n");
243         exit(1);
244     } else {
245         format = ZPixmap;
246         bitmap_pad = 8;
247         if (depth > 8) bitmap_pad = 32;
248     }
249 
250     visual = DefaultVisual(display, screen);
251 
252 /* Set default values */
253     size  =  SIZE;
254     x_pos =  X_POS;
255     y_pos =  Y_POS;
256 
257     if (opt_geometry > 0) {
258         if (mask & WidthValue) size = width_return;
259         if (mask & HeightValue) size = height_return;
260         if (size < MIN_SIZE) {
261             fprintf(stderr, "xworld: warning: using minumum size of %d in"
262               " lieu of user supplied value.\n", MIN_SIZE);
263             size = MIN_SIZE;
264         }
265         if (mask & XValue) {
266             if (mask & XNegative)
267               x_pos = DisplayWidth(display, screen) - size + x_return;
268             else
269               x_pos = x_return;
270         }
271         if (mask & YValue) {
272             if (mask & YNegative)
273               y_pos = DisplayHeight(display, screen) - size + y_return;
274             else
275               y_pos = y_return;
276         }
277     }
278     old_size = size;
279     n = size - 2*BORDER;
280     r1 = n/2;
281     r2 = size/2;
282 
283     foreground = WhitePixel(display, screen);
284     background = BlackPixel(display, screen);
285 
286     image = XCreateImage(display, visual, depth, format, 0, 0, size, size,
287       bitmap_pad, 0);
288     if (image == 0) {
289         fprintf(stderr, "xworld: can't create XImage\n");
290         exit(1);
291     }
292 
293     xswa.event_mask = ExposureMask | StructureNotifyMask;
294     xswa.background_pixel = BlackPixel(display, screen);
295     xswamask = CWEventMask | CWBackPixel;
296 
297     class   = InputOutput;
298     win = XCreateWindow(display, RootWindow(display, screen), x_pos, y_pos,
299       size, size, border, depth, class, visual, xswamask, &xswa);
300 
301     if (XStringListToTextProperty(MainTitle, 1, &xtptitle))
302       XSetWMName(display, win, &xtptitle);
303 
304     if (XStringListToTextProperty(IconTitle, 1, &xtptitle))
305       XSetWMIconName(display, win, &xtptitle);
306 
307     xsh.flags = USPosition | USSize | PPosition | PSize | PMinSize | PAspect;
308     xsh.x = x_pos;
309     xsh.y = y_pos;
310     xsh.min_width = MIN_SIZE;
311     xsh.base_width = size;
312     xsh.min_height = MIN_SIZE;
313     xsh.base_height = size;
314     xsh.min_aspect.x = 1;
315     xsh.min_aspect.y = 1;
316     xsh.max_aspect.x = 1;
317     xsh.max_aspect.y = 1;
318 
319     XSetWMHints(display, win, &xwmh);
320     XSetWMNormalHints(display, win, &xsh);
321 
322 /* Allocate colors */
323     cmap = DefaultColormap(display, screen);
324     for (i = 0; i < NCOLORS; i++) {
325         xcolor[i].red = color[i].red;
326         xcolor[i].green = color[i].green;
327         xcolor[i].blue = color[i].blue;
328         if (XAllocColor(display, cmap, &xcolor[i]) == 0) {
329             fprintf(stderr, "xworld: can't allocate colors\n");
330 #if 0
331             exit(-1);
332 #endif
333         }
334     }
335 
336 /*
337  * generate startup image
338  */
339     Map = alloc_image(image);
340 
341     gc = XCreateGC(display, win, 0, 0);
342 
343     InputSelectMask = ExposureMask | StructureNotifyMask;
344     XSelectInput(display, win, InputSelectMask);
345     XPutImage(display, win, gc, image, 0, 0, 0, 0, size, size);
346     XMapWindow(display, win);
347 
348     Earth.SphereBuffer = addr;
349     Earth.CoLatitude = CoLatitude;
350     Earth.PointSeparation = PointSeparation;
351     Earth.FirstPointOfLatitude = FirstPointOfLatitude;
352     Earth.PointsPerLatitude = PointsPerLatitude;
353     Earth.DeltaLatitude = PI/NINTERVALS;
354     Earth.LastPoint = Earth.FirstPointOfLatitude[NINTERVALS];
355     Earth.Dummy = Dummy;
356 
357 /*
358  * begin of main loop -- xworld will run until the process is killed
359  */
360     for ( ; ; ) {
361 
362 /*
363  * read the clock
364  */
365         clock = time((time_t *)NULL);
366         if (clock == (time_t)-1) {
367             fprintf(stderr, "xworld: can't read clock\n");
368             exit(1);
369         }
370 
371 /*
372  * deal with gm time
373  */
374         gmt = gmtime(&clock);
375         jr = gmt->tm_year + 1900;
376         m = gmt->tm_mon + 1;
377         d = gmt->tm_mday;
378         hr = gmt->tm_hour;
379         min = gmt->tm_min;
380         sec = gmt->tm_sec;
381 
382 /*
383  * do some basic astronomy
384  */
385         juldat = (double)JulianDate(jr, m, d) - 0.5;
386         daypart = (sec + 60*(min + 60*hr))/86400.0;
387         juldat += daypart;
388         phi = daypart*TWO_PI + EqunofTime(juldat, &sunra, &sundec);
389 
390         sint = sin(sundec);
391         cost = cos(sundec);
392         sinp = sin(PI - phi);
393         cosp = cos(PI - phi);
394         m11 = -sinp;
395         m12 = -cosp*sint;
396         m13 =  cosp*cost;
397         m21 =  cosp;
398         m22 = -sinp*sint;
399         m23 =  sinp*cost;
400 /*      m31 =  0.0;  therfore we omit it here */
401         m32 =  cost;
402         m33 =  sint;
403 
404         j = -1;
405         for (iy = 0; iy < size; iy++) {
406             y = (double)(r2 - iy)/r1;
407             y2 = y*y;
408             for (ix = 0; ix < size; ix++) {
409                 j++;
410                 x = (double)(ix - r2)/r1;
411                 x2 = x*x;
412                 z2 = (double)1.0 - (x2 + y2);
413                 if (z2 >= (double)0.0) {
414                     z = sqrt(z2);
415                     xp = m11*x + m12*y + m13*z;
416                     yp = m21*x + m22*y + m23*z;
417                     zp =         m32*y + m33*z;
418                     tTheta = acos(zp);
419                     tPhi = atan2(yp, xp);
420                     i = GetSpherePoint(&Earth, &Value, tTheta, tPhi);
421 
422 /*
423  * Map elevations to colors
424  */
425                     if (i != SOK) {
426 			XPutPixel(image, ix, iy, xcolor[BLACK].pixel);
427                         continue;
428                     }
429                     if (Value > level[NLEVELS - 1]) {
430 			XPutPixel(image, ix, iy, xcolor[HIGH].pixel);
431                         continue;
432                     }
433                     for (i = 0; i <= NLEVELS - 1; i++) {
434                         if (Value <= level[i]) {
435 			    XPutPixel(image, ix, iy, xcolor[i].pixel);
436                             break;
437                         }
438                     }
439                 } else
440 		    XPutPixel(image, ix, iy, xcolor[BLACK].pixel);
441             }
442         }
443         XPutImage(display, win, gc, image, 0, 0, 0, 0, size, size);
444         for (i = 0; i < SLEEP; i++) {
445             sleep(1);
446             if (XCheckMaskEvent(display, InputSelectMask, &event)) {
447                 if (event.type == Expose && event.xexpose.count == 0) {
448                     while (XCheckTypedEvent(display, Expose, &event));
449                     XPutImage(display, win, gc, image, 0, 0, 0, 0, size, size);
450                 }
451                 if (event.type == ConfigureNotify) {
452                     while (XCheckTypedEvent(display, ConfigureNotify, &event));
453                     ConfigureEvent = (XConfigureEvent *)&event;
454                     size = ConfigureEvent->width;
455                     if (ConfigureEvent->height != size) {
456 #if 0
457                         fprintf(stderr, "xworld: error: width != height\n");
458                         exit(1);
459 #else
460 			size = MIN(size, ConfigureEvent->height);
461 #endif
462                     }
463                     if (size != old_size) {
464                         old_size = size;
465                         n = size - 2*BORDER;
466                         r1 = n/2;
467                         r2 = size/2;
468                         XDestroyImage(image);
469                         image = XCreateImage(display, visual, depth, format,
470                           0, 0, size, size, bitmap_pad, 0);
471                         if (image == NULL) {
472                             fprintf(stderr, "xworld: can't create XImage\n");
473                             exit(1);
474                         }
475 			Map = alloc_image(image);
476 
477                         XPutImage(display, win, gc, image, 0, 0, 0, 0,
478                           size, size);
479                         break;
480                     }
481                 }
482             }
483         }
484     }
485 }
486